/[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.11 - (show annotations)
Mon Jun 23 17:54:32 2003 UTC (21 years ago) by joko
Branch: MAIN
Changes since 1.10: +16 -1 lines
prepared execution of in-process perl-code via eval (not activated yet!)

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

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