/[cvs]/nfo/perl/libs/Data/Rap/Engine.pm
ViewVC logotype

Contents of /nfo/perl/libs/Data/Rap/Engine.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.13 - (show annotations)
Fri Dec 5 05:02:08 2003 UTC (20 years, 7 months ago) by joko
Branch: MAIN
Changes since 1.12: +10 -6 lines
+ minor update: disabled some unnecessary loggers or changed to debug-level

1 ## ----------------------------------------------------------------------
2 ## $Id: Engine.pm,v 1.12 2003/06/24 20:59:51 jonen Exp $
3 ## ----------------------------------------------------------------------
4 ## $Log: Engine.pm,v $
5 ## Revision 1.12 2003/06/24 20:59:51 jonen
6 ## added option 'detach'
7 ##
8 ## Revision 1.11 2003/06/23 17:54:32 joko
9 ## prepared execution of in-process perl-code via eval (not activated yet!)
10 ##
11 ## Revision 1.10 2003/05/13 07:56:12 joko
12 ## enhanced: *hierarchical* containers for context handling
13 ## fixes: some pre-flight checks
14 ## new: propagate "end-tag" event to e.g. close containers
15 ##
16 ## Revision 1.9 2003/04/04 17:23:11 joko
17 ## minor update: debugging output
18 ##
19 ## Revision 1.8 2003/03/29 07:11:54 joko
20 ## modified: sub run_executable
21 ## new: sub run_script
22 ##
23 ## Revision 1.7 2003/03/28 07:02:56 joko
24 ## modified structure around '$wrapper_program'
25 ##
26 ## Revision 1.6 2003/03/27 15:31:05 joko
27 ## fixes to modules regarding new namespace(s) below Data::Mungle::*
28 ##
29 ## Revision 1.5 2003/03/27 15:03:03 joko
30 ## enhanced 'sub run_executable'
31 ##
32 ## Revision 1.4 2003/02/22 16:51:21 joko
33 ## + enhanced run_executable
34 ## modified logging output
35 ##
36 ## Revision 1.3 2003/02/21 01:46:17 joko
37 ## renamed core function
38 ##
39 ## Revision 1.2 2003/02/20 19:46:33 joko
40 ## renamed and revamped some of modules
41 ## renamed methods
42 ## + sub run_executable
43 ##
44 ## Revision 1.1 2003/02/18 15:35:25 joko
45 ## + initial commit
46 ##
47 ## ----------------------------------------------------------------------
48
49
50 package Data::Rap::Engine;
51
52 use strict;
53 use warnings;
54
55
56 use Data::Dumper;
57 use Hash::Merge qw( merge );
58 use Iterate;
59
60 use shortcuts qw( run_cmd );
61 use Data::Mungle::Code::Ref qw( ref_slot );
62 use Data::Mungle::Transform::Deep qw( expand deep_copy );
63 use File::Temp qw/ tempfile tempdir /;
64
65 my $DEBUG = 0;
66
67 sub performTarget {
68 my $self = shift;
69 my $targetname = shift;
70 $self->perform_target($targetname);
71 }
72
73 sub perform_target {
74 my $self = shift;
75 my $targetname = shift;
76
77 # pre-flight checks
78 if (!$targetname) {
79 $self->log("Target name empty. Please try to specify (e.g.) on the command line.", 'critical');
80 return;
81 }
82
83 my $header = ("- " x 12) . " " . $targetname . " " . ("- " x 6);
84
85 # V1
86 #$self->log("- " x 35, 'notice');
87 #$self->log("Performing Target '$targetname'.", 'notice');
88
89 # V2
90 #$self->log($header, 'notice');
91
92 # V3
93 #$self->log("- " x 20, 'info');
94 $self->log("Performing Target '$targetname'.", 'notice');
95
96 #exit;
97
98 my $target = $self->getTargetDetails($targetname);
99
100 # trace
101 #print Dumper($target);
102 #exit;
103
104 $self->perform_dependencies($target);
105 $self->perform_details($target);
106
107 return 1;
108
109 }
110
111 sub perform_dependencies {
112 my $self = shift;
113 my $target = shift;
114 # resolve dependencies (just run prior)
115 if (my $targetname_dep = $target->{attrib}->{depends}) {
116 my @targets = split(/,\s|,/, $targetname_dep);
117 #print Dumper(@targets);
118 #$self->perform($targetname_dep);
119 #delete $target->{attrib}->{depends};
120 foreach (@targets) {
121 if (!$self->{__rap}->{dependencies}->{resolved}->{$_}++) {
122 $self->perform_target($_);
123 }
124 }
125 delete $target->{attrib}->{depends};
126 }
127 }
128
129 sub perform_details {
130 my $self = shift;
131 my $target = shift;
132
133 #print Dumper($target);
134 #exit;
135
136 foreach my $entry (@{$target->{content}}) {
137 my $command = $entry->{name};
138 my $args = $entry->{attrib};
139 my $content = $entry->{content};
140 $self->perform_command($command, $args, $content, { warn => 1 } );
141 # check recursiveness
142 # new condition: don't recurse if node is flagged to have inline-args (2003-04-17)
143 my $has_inline_args = ($entry->{attrib}->{_args} && $entry->{attrib}->{_args} eq 'inline');
144 if ($entry->{content} && ref $entry->{content} && !$has_inline_args) {
145 $self->perform_details($entry);
146 }
147 # new of 2003-05-08
148 $command ||= '';
149 $self->perform_command($command . '_end', undef, undef, { warn => 0 } );
150 }
151 }
152
153 sub rc {
154 my $self = shift;
155 return $self->perform_command(@_);
156 }
157
158 sub perform_command {
159 my $self = shift;
160 my $command = shift;
161 my $args_list = shift;
162 my $content = shift;
163 my $options = shift;
164
165 if (!$command) {
166 $self->log("Command was empty!", 'debug') if $DEBUG;
167 return;
168 }
169
170 # FIXME: make '__PACKAGE__' go one level deeper properly!
171 $self->log( __PACKAGE__ . "->perform_command: " . $command, 'debug') if $DEBUG;
172
173
174 # 1. make arguments from list of arguments(?)
175
176 my $args = {};
177 #print Dumper($args_list);
178 if ($args_list) {
179 if (ref $args_list eq 'ARRAY') {
180 foreach (@$args_list) {
181 $args = merge($args, $_);
182 }
183 } else {
184 $args = $args_list;
185 }
186 }
187
188
189 # 2. prepare command
190
191 # default setting for internal rap commands
192 my $method_prefix_default = '_';
193 # setting from property database
194 my $method_prefix_setting = $self->get_property('core.method_prefix');
195 #print "setting: ", $method_prefix_setting, "\n";
196 my $prefix = $method_prefix_setting;
197 if (not defined $prefix) {
198 $prefix = $method_prefix_default;
199 }
200 $command = $prefix . $command;
201
202
203 # 3. determine container
204 my $container; # = $self->getInstance();
205 #$container ||= $self->getInstance();
206 $container ||= $self;
207
208 # 4. run method
209 if ($container->can($command)) {
210 $container->$command($args, $content);
211 } else {
212 my $level = "debug";
213 $level = "warning" if $options->{warn};
214 $self->log("Command '$command' not implemented.", $level) if $DEBUG;
215 }
216
217 }
218
219
220 sub merge_properties {
221 my $self = shift;
222 my $name = shift;
223 my $data = shift;
224
225 $self->log("merge-name: $name");
226 #print "name: $name", "\n";
227 #print Dumper($data);
228 #exit;
229
230 # check if slot (or childs of it) is/are already occupied
231 #if (my $data_old = ref_slot($self, $name, undef, '.')) {
232 if (my $data_old = $self->get_property($name)) {
233 #print "old:", "\n";
234 #print Dumper($data_old);
235
236 # FIXME: review - any options for 'merge' here?
237 my $data_new = merge($data_old, $data);
238 #print "DATA NEE! - MERGE!", "\n";
239 #print Dumper($data_new);
240 #exit;
241 #merge_to($self, $data_new);
242 $self->set_property( { name => $name, value => $data_new } );
243
244 } else {
245
246 =pod
247 # make up a dummy hash matching the structure of the destination one
248 my $dummy = {};
249 ref_slot($dummy, $name, $data, '.');
250 print Dumper($dummy);
251
252 # mix into destination
253 mixin($self, $dummy, { init => 1 });
254 =cut
255
256 ref_slot($self, $name, $data, '.');
257
258 }
259
260
261 #print Dumper($self);
262 #exit;
263 #$self->
264
265 }
266
267 sub set_property {
268 my $self = shift;
269 my $args = shift;
270
271 $self->interpolate($args) ;
272 my $name = $args->{name};
273 my $value = $args->{value};
274 $name = '__rap.properties.' . $name;
275
276 $self->log("set-name: $name");
277
278 #print Dumper($name, $value, '.');
279
280 # fill property slot with given value
281 # fix (2003-04-17): always do fill if value is *defined*!!!
282 if (defined $value) {
283 ref_slot($self, $name, $value, '.');
284 }
285
286 #print Dumper($self);
287
288 # FIXME!!!
289 #return if ! ref $args;
290
291 # fill property slot with (probably bigger) data structure from property-/configuration-style - file
292 if (my $file = $args->{file}) {
293 my $type = $args->{type};
294 if (!$type) {
295 die("default file (no type specified) is not implemented yet!");
296
297 } elsif ($type eq 'perl-eval') {
298 #die($type);
299 $self->loadFromPerlFile($file, $name, $args->{'varnames'});
300
301 } elsif ($type eq 'App::Config') {
302 die("not implemented: $type");
303
304 }
305
306 } elsif (my $command = $args->{command}) {
307 $self->perform_command($command, $args);
308
309 }
310
311 }
312
313 sub get_property {
314 my $self = shift;
315 #my $args = shift;
316 my $name = shift;
317
318 #$self->interpolate($args);
319
320 #my $name = $args->{name};
321 my $result;
322
323 if (!$name) {
324 $self->log( __PACKAGE__ . ": no name!", 'critical');
325
326 } elsif ($name eq '/') {
327 $result = expand($self);
328
329 } elsif ($name eq '.') {
330 if (my $instance = $self->getInstance()) {
331 $result = expand($instance);
332 } else {
333 $result = ref_slot($self, '__rap.properties', undef, '.');
334
335 }
336
337 } else {
338
339 $name = '__rap.properties.' . $name;
340
341 $self->log("get-name: $name") if $DEBUG;
342
343 # get property slot and return value
344 $result = ref_slot($self, $name, undef, '.');
345
346 # FIXME: Is this okay? It's provided for now in order not
347 # to deliver an 'undef' to the regex below inside 'interpolate'.
348 # revamp this, maybe!
349 #$result ||= ''; # NO!!!
350 }
351
352 return $result;
353 }
354
355 sub interpolate {
356 my $self = shift;
357 my $ref = shift;
358 IterHash %$ref, sub {
359 #print $_[1], "\n";
360 $_[1] =~ s/\${(.+)}/$self->get_property($1)/e;
361 }
362 }
363
364 sub run_executable {
365 my $self = shift;
366 my $opts = shift;
367
368 my $meta = deep_copy($opts);
369
370 delete $opts->{caption};
371 delete $opts->{async};
372 delete $opts->{detach};
373
374 #print Dumper($meta);
375
376 if ($opts->{executable}) {
377
378 my $program = $opts->{executable};
379 delete $opts->{executable};
380
381 # determine execution method
382 my $wrapper_program = '';
383
384 # check if program is a namespace-string (contains '::') - use 'do' in this case!
385 if ($program =~ /::/) {
386 #$wrapper_program = 'rap.pl';
387 $wrapper_program = $0;
388 }
389
390 # prepare arguments
391 my @buf;
392 foreach (keys %$opts) {
393 my $value = $opts->{$_};
394 if (m/^_/) {
395 if ($_ eq '_switches') {
396 my @switches = split(/,\s|,/, $value);
397 foreach my $switch (@switches) {
398 push @buf, '--' . $switch;
399 }
400 }
401 next;
402 }
403
404 if ($value =~ /\s/) {
405 $value = "\"$value\"";
406 }
407 push @buf, "--$_=$value";
408 }
409
410 # build {program} & {arguments}
411 my $cmd = ($wrapper_program ? $wrapper_program . ' ' : '') . $program . ' ' . join(' ', @buf);
412
413 # trace
414 #print "command: $cmd", "\n";
415
416 # start process
417 # V1: via shortcut
418 #$ENV{PERL5LIB} = join(' ', @INC);
419
420 # FIXME!!! what about the other slots of @INC?
421 $ENV{PERL5LIB} = $INC[0];
422
423 #print Dumper(%ENV);
424
425 #print "command: '$cmd'", "\n";
426
427 # V1 - basic
428 #run_cmd($cmd);
429
430 # V1.b - enhanced: variable local method
431 $meta->{caption} ||= '';
432 $meta->{async} ||= 0;
433 $meta->{detach} ||= 0;
434 # new of 2003-05-08: USE_PATH!
435 $meta->{USE_PATH} ||= 0;
436 my $evalstr = "run_cmd('$cmd', '$meta->{caption}', { async => $meta->{async}, detach => $meta->{detach}, USE_PATH => $meta->{USE_PATH} });";
437 eval($evalstr);
438 #my $res = do "$cmd";
439 #print $res, "\n" if $res;
440
441 #$self->log("run_executable: $evalstr", 'info');
442 $self->raiseException("run_executable: $evalstr\n$@") if $@;
443
444 # V2: via IPC::Run
445 # .... (TODO)
446
447
448 }
449
450 }
451
452
453 sub run_script {
454
455 my $self = shift;
456 my $args = shift;
457 my $code = shift;
458
459 if ($args->{language} eq 'msdos/bat') {
460
461 #print "code: $code", "\n";
462
463 # reporting
464 $self->log("Executing some arbitrary unsigned code (probably unsafe). [language=$args->{language}]", 'info');
465 $self->log("\n<code>\n$code\n</code>", 'info');
466
467 # create temporary intermediate file to execute code
468 my $tmpdir = '/tmp/rap';
469 mkdir $tmpdir;
470 (my $fh, my $filename) = tempfile( DIR => $tmpdir, SUFFIX => '.bat' );
471 print $fh $code, "\n";
472 run_cmd( $filename, '', { async => 1 } );
473
474 # FIXME: DELETE code inside temp-files as soon as possible!
475 #print $fh '';
476
477 # TODO: delete FILE completely!
478 # required for this: wait until execution has finished, then unlink....
479 # but: "how to wait until execution is finished"?
480 # i believe the best is to spawn *another* process directly from here,
481 # let's call it 'watcher-agent'.
482 # This one should monitor a certain running process and delete its
483 # executable file after it has finished execution.
484 # Possible extensions could be:
485 # keep track of all stuff sent to STDOUT or STDERR and
486 # send that stuff to the task-owner indirectly (not via shell/console)
487 # (e.g. via email, by posting report to a newsgroup or publishing on a specified web-page: use mod-dav!)
488
489 } elsif ($args->{language} eq 'bash') {
490 $self->log("FIXME: - - - - - -- - - -- BASH - - - - - - - -- - ", 'error');
491
492 } elsif ($args->{language} eq 'perl') {
493
494 # reporting
495 #$self->log("Executing some arbitrary unsigned code (probably unsafe). [language=$args->{language}]", 'info');
496 #$self->log("\n<code>\n$code\n</code>", 'info');
497
498 # do it
499 #eval($code);
500 #$self->log("\n<code>\n$code\n</code>", 'error') if $@;
501
502 } else {
503 $self->log("FIXME: Script language '$args->{language}' not implemented.", 'error');
504
505 }
506
507 }
508
509 1;
510 __END__

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed