/[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.15 - (show annotations)
Wed Jun 16 16:37:59 2004 UTC (20 years ago) by joko
Branch: MAIN
Changes since 1.14: +12 -6 lines
attempt to get things going in a generic way (Linux/FreeBSD/Win32)

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

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