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   101908 use strict;
  278         659  
  278         8073  
4 278     278   1376 use warnings;
  278         540  
  278         7294  
5              
6 278     278   1426 use Carp 'confess';
  278         601  
  278         12502  
7 278     278   1711 use Scalar::Util 'weaken';
  278         607  
  278         13760  
8 278     278   1800 use File::Path 'mkpath';
  278         629  
  278         20402  
9 278     278   2177 use File::Basename 'dirname', 'basename';
  278         761  
  278         15178  
10              
11 278     278   7746 use SPVM ();
  278         634  
  278         7715  
12              
13 278     278   145514 use SPVM::Builder::CC;
  278         1091  
  278         11336  
14 278     278   120440 use SPVM::Builder::Compiler;
  278         813  
  278         8788  
15 278     278   122139 use SPVM::Builder::Runtime;
  278         753  
  278         8756  
16 278     278   113734 use SPVM::Builder::Env;
  278         1088  
  278         9194  
17 278     278   111142 use SPVM::Builder::Stack;
  278         1064  
  278         360131  
18              
19             # Fields
20             sub build_dir {
21 839     839 0 2021 my $self = shift;
22 839 50       2665 if (@_) {
23 0         0 $self->{build_dir} = $_[0];
24 0         0 return $self;
25             }
26             else {
27 839         3013 return $self->{build_dir};
28             }
29             }
30              
31             sub include_dirs {
32 2023     2023 0 7886 my $self = shift;
33 2023 50       5243 if (@_) {
34 0         0 $self->{include_dirs} = $_[0];
35 0         0 return $self;
36             }
37             else {
38 2023         11974 return $self->{include_dirs};
39             }
40             }
41              
42             sub new {
43 2090     2090 0 3481760 my $class = shift;
44            
45             my $self = {
46 2090         7639 include_dirs => [map { "$_/SPVM" } @INC],
  24489         61079  
47             @_
48             };
49            
50 2090   33     12984 bless $self, ref $class || $class;
51            
52 2090         5953 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 1296 my ($self, $basic_type_name, $options) = @_;
144            
145 329   50     1317 $options ||= {};
146            
147 329         2012 my $build_dir = $self->build_dir;
148            
149 329         1000 my $dl_func_list = $options->{dl_func_list};
150 329         799 my $class_file = $options->{class_file};
151 329         1304 my $precompile_source = $options->{precompile_source};
152              
153 329         894 my $category = $options->{category};
154            
155             # Build directory
156 329 50       1267 if (defined $build_dir) {
157 329         22780 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         1413 my $build_src_dir;
165 329 100       1751 if ($category eq 'precompile') {
    50          
166 307         2753 $build_src_dir = SPVM::Builder::Util::create_build_src_path($build_dir);
167 307         9411 mkpath $build_src_dir;
168            
169 307         4868 my $cc = SPVM::Builder::CC->new(
170             build_dir => $build_dir,
171             at_runtime => 1,
172             );
173            
174 307         3253 $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         76 my $class_file = $options->{class_file};
185 22         165 $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         3064 my $build_object_dir = SPVM::Builder::Util::create_build_object_path($build_dir);
190 329         14720 mkpath $build_object_dir;
191            
192             # Lib directory
193 329         3549 my $build_lib_dir = SPVM::Builder::Util::create_build_lib_path($build_dir);
194 329         8855 mkpath $build_lib_dir;
195            
196 329         4824 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         20934 return $build_file;
210             }
211              
212             sub build {
213 329     329 0 1340 my ($self, $basic_type_name, $options) = @_;
214            
215 329   50     1934 $options ||= {};
216            
217 329         1271 my $build_dir = $self->build_dir;
218            
219 329         1330 my $at_runtime = $options->{at_runtime};
220 329         2464 my $cc = SPVM::Builder::CC->new(
221             build_dir => $build_dir,
222             at_runtime => $at_runtime,
223             );
224            
225 329         1083 my $dl_func_list = $options->{dl_func_list};
226            
227 329         936 my $category = $options->{category};
228            
229             # Class file
230 329         792 my $class_file = $options->{class_file};
231 329 50       1251 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         682 my $config;
243 329 100       2200 if ($category eq 'native') {
    50          
244 22         121 $config = $self->create_native_config_from_class_file($class_file);
245             }
246             elsif ($category eq 'precompile') {
247 307         2923 $config = SPVM::Builder::Util::API::create_default_config();
248             }
249            
250 329         2308 $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         1962 config => $config,
257             category => $category,
258             };
259              
260 329         2998 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         8557 config => $config,
266             category => $category,
267             dl_func_list => $dl_func_list,
268             };
269 329         6107 my $output_file = $cc->link(
270             $basic_type_name,
271             $object_files,
272             $link_options
273             );
274            
275 329         59407 return $output_file;
276             }
277              
278             sub create_native_config_from_class_file {
279 23     23 0 93 my ($self, $class_file) = @_;
280            
281 23         44 my $config;
282 23         61 my $config_file = $class_file;
283 23         240 $config_file =~ s/\.spvm$/.config/;
284              
285             # Config file
286 23 50       855 if (-f $config_file) {
287 23         425 $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         87 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