/[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.12 - (show annotations)
Tue Jun 24 20:59:51 2003 UTC (21 years ago) by jonen
Branch: MAIN
Changes since 1.11: +9 -3 lines
added option 'detach'

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

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