/[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.10 - (show annotations)
Tue May 13 07:56:12 2003 UTC (21 years, 1 month ago) by joko
Branch: MAIN
Changes since 1.9: +49 -8 lines
enhanced: *hierarchical* containers for context handling
fixes: some pre-flight checks
new: propagate "end-tag" event to e.g. close containers

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

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