|
1 # Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
2 # All rights reserved. |
|
3 # This component and the accompanying materials are made available |
|
4 # under the terms of "Eclipse Public License v1.0" |
|
5 # which accompanies this distribution, and is available |
|
6 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 # |
|
8 # Initial Contributors: |
|
9 # Nokia Corporation - initial contribution. |
|
10 # |
|
11 # Contributors: |
|
12 # |
|
13 # Description: |
|
14 # |
|
15 #!/usr/bin/perl |
|
16 |
|
17 use strict; |
|
18 |
|
19 |
|
20 use FindBin; # for FindBin::Bin |
|
21 use lib $FindBin::Bin; |
|
22 use lib "$FindBin::Bin/lib"; |
|
23 |
|
24 use Cwd; |
|
25 use Cwd 'abs_path'; |
|
26 use Getopt::Long; |
|
27 use File::Basename; |
|
28 use File::Spec; |
|
29 use XML::DOM; |
|
30 |
|
31 my $output; |
|
32 my $path; |
|
33 my @config; |
|
34 my @includes; |
|
35 my %defineParams; |
|
36 my %defines; |
|
37 my $defaultns = 'http://www.symbian.org/system-definition'; # needed if no DTD |
|
38 my @excludeMetaList; |
|
39 my @cannotExclude= ('link-mapping', 'config'); |
|
40 my %ID; # list of all IDs |
|
41 |
|
42 my @newarg; |
|
43 foreach my $a (@ARGV) |
|
44 { #extract all -I parameters from the parameter list |
|
45 if($a=~s/^-I//) |
|
46 { |
|
47 push(@includes,$a); |
|
48 } |
|
49 else |
|
50 { |
|
51 push(@newarg,$a); |
|
52 } |
|
53 } |
|
54 @ARGV=@newarg; |
|
55 |
|
56 # need to add options for controlling which metas are filtered out and which are included inline |
|
57 GetOptions |
|
58 ( |
|
59 'path=s' => \$path, |
|
60 'output=s' => \$output, |
|
61 'config=s' => \@config, |
|
62 'exclude-meta=s' => \@excludeMetaList |
|
63 ); |
|
64 |
|
65 # -path specifies the full system-model path to the file which is being processed. |
|
66 # This must be an absolute path if you're processing a root sysdef. |
|
67 # If processing a pkgdef file, you can use "./package_definition.xml" to leave all links relative. Though I can't really see the use case for this. |
|
68 |
|
69 # -output specifies the file to save the output to. If not specified this will write to stdout |
|
70 |
|
71 # -config specifies the name of an .hrh file in which the configuration data is acquired from. If not set, no confguration will be done. |
|
72 |
|
73 # -I[path] specifies the include paths to use when resolving #includes in the .hrh file. Same syntax as cpp command uses. Any number of these can be provided. |
|
74 |
|
75 |
|
76 # if config is not set, no confguration will be done. |
|
77 # If it is set, all configuration metadata will be processed and stripped from the output, even if the confguration data is empty |
|
78 |
|
79 if($path eq '') {$path = '/os/deviceplatformrelease/foundation_system/system_model/system_definition.xml'} |
|
80 |
|
81 ($#ARGV == -1 ) && &help(); |
|
82 my $sysdef = &abspath(shift); # resolve the location of the root sysdef |
|
83 |
|
84 |
|
85 my %excludeMeta; |
|
86 foreach (@excludeMetaList) {$excludeMeta{$_}=1} # make list a hash table |
|
87 foreach (@cannotExclude) |
|
88 { |
|
89 $excludeMeta{$_} && print STDERR "Error: Cannot exclude meta rel=\"$_\"\n"; |
|
90 $excludeMeta{$_}=0 |
|
91 } # cannot exclude any of these rel types |
|
92 |
|
93 |
|
94 # rootmap is a mapping from the filesystem to the paths in the doc |
|
95 my %rootmap = &rootMap($path,$sysdef); |
|
96 my %nsmap; |
|
97 my %urimap; |
|
98 |
|
99 foreach my $conf (@config) |
|
100 { # run cpp to get all #defines |
|
101 &getDefines($conf); |
|
102 } |
|
103 |
|
104 my $parser = new XML::DOM::Parser; |
|
105 my $sysdefdoc = $parser->parsefile ($sysdef); |
|
106 |
|
107 |
|
108 my $maxschema = $sysdefdoc->getDocumentElement()->getAttribute('schema'); # don't check value, just store it. |
|
109 |
|
110 |
|
111 # find all the namespaces used in all trhe fragments and use that |
|
112 # to set the namespaces ni the root element of the created doc |
|
113 # should be able to optimise by only parsing each doc once and |
|
114 # maybe skipping the contends of <meta> |
|
115 my @nslist = &namespaces($sysdef,$sysdefdoc->getDocumentElement()); |
|
116 |
|
117 |
|
118 while(@nslist) |
|
119 { |
|
120 my $uri = shift(@nslist); |
|
121 my $prefix =shift(@nslist); |
|
122 if($prefix eq 'id namespace'){$prefix=''} |
|
123 if(defined $urimap{$uri}) {next} # already done this uri |
|
124 $urimap{$uri} = $prefix; |
|
125 if($nsmap{$prefix}) |
|
126 { # need a new prefix for this, guess from the URI (for readability) |
|
127 if($uri=~/http:\/\/(www\.)?([^.\/]+)\./) {$prefix = $2} |
|
128 my $i=0; |
|
129 while($nsmap{$prefix}) |
|
130 { # still no prefix, just make up |
|
131 $prefix="ns$i"; |
|
132 $i++; |
|
133 # next line not really necessary, but it's a good safety to stop infinite loops |
|
134 $i eq 1000 && die "cannot create namespace prefix for $uri"; |
|
135 } |
|
136 } |
|
137 $nsmap{$prefix}=$uri; |
|
138 } |
|
139 |
|
140 my $docroot = $sysdefdoc->getDocumentElement; |
|
141 |
|
142 my $ns = $docroot->getAttribute('id-namespace'); |
|
143 if(!$ns && $nsmap{''}) |
|
144 { |
|
145 $docroot->setAttribute('id-namespace',$nsmap{''}); |
|
146 } |
|
147 |
|
148 $docroot->setAttribute('schema',$maxschema); # output has the largest syntax version of all includes |
|
149 |
|
150 |
|
151 while(my($pre,$uri) = each(%nsmap)) |
|
152 { |
|
153 $pre ne '' || next ; |
|
154 $docroot->setAttribute("xmlns:$pre",$uri); |
|
155 } |
|
156 |
|
157 &walk($sysdef,$docroot); # process the XML |
|
158 |
|
159 |
|
160 # print to file or stdout |
|
161 if($output eq '') |
|
162 { |
|
163 print $sysdefdoc->toString; |
|
164 } |
|
165 else |
|
166 { |
|
167 $sysdefdoc->printToFile($output); |
|
168 } |
|
169 |
|
170 |
|
171 sub abspath |
|
172 { # normalize the path into an absolute one |
|
173 my ($name,$path) = fileparse($_[0]); |
|
174 $path=~tr,\\,/,; |
|
175 if( -e $path) |
|
176 { |
|
177 return abs_path($path)."/$name"; |
|
178 } |
|
179 my @dir = split('/',$_[0]); |
|
180 my @new; |
|
181 foreach my $d (@dir) |
|
182 { |
|
183 if($d eq '.') {next} |
|
184 if($d eq '..') |
|
185 { |
|
186 pop(@new); |
|
187 next; |
|
188 } |
|
189 push(@new,$d) |
|
190 } |
|
191 return join('/',@new); |
|
192 } |
|
193 |
|
194 sub rootMap { |
|
195 my @pathdirs = split(/\//,$_[0]); |
|
196 my @rootdirs = split(/\//,$_[1]); |
|
197 |
|
198 while(lc($rootdirs[$#rootdirs]) eq lc($pathdirs[$#pathdirs]) ) |
|
199 { |
|
200 pop(@rootdirs); |
|
201 pop(@pathdirs); |
|
202 } |
|
203 return (join('/',@rootdirs) => join('/',@pathdirs) ); |
|
204 } |
|
205 |
|
206 sub rootMapMeta { |
|
207 # find all the explict path mapping from the link-mapping metadata |
|
208 my $node = shift; |
|
209 foreach my $child (@{$node->getChildNodes}) |
|
210 { |
|
211 if ($child->getNodeType==1 && $child->getTagName eq 'map-prefix') |
|
212 { |
|
213 my $from = $child->getAttribute('link'); |
|
214 my $to = $child->getAttribute('to'); # optional, but blank if not set |
|
215 $rootmap{$from} = $to; |
|
216 } |
|
217 } |
|
218 # once this is processed we have no more need for it. Remove from output |
|
219 $node->getParentNode->removeChild($node); |
|
220 } |
|
221 |
|
222 |
|
223 sub walk |
|
224 { # walk through the doc, resolving all links |
|
225 my $file = shift; |
|
226 my $node = shift; |
|
227 my $type = $node->getNodeType; |
|
228 if($type!=1) {return} |
|
229 my $tag = $node->getTagName; |
|
230 if($tag=~/^(layer|package|collection|component)$/ ) |
|
231 { |
|
232 if($file eq $sysdef) |
|
233 { |
|
234 &fixIDs($node); # normalise all IDs in the root doc. Child docs are handled elsewhere. |
|
235 } |
|
236 my $link= $node->getAttribute('href'); |
|
237 if($link) |
|
238 { |
|
239 my $file = &resolvePath($file,$link); |
|
240 if(-e $file) |
|
241 { |
|
242 &combineLink($node,$file); |
|
243 } |
|
244 else |
|
245 { |
|
246 print STDERR "Note: $file not found\n"; |
|
247 $node->removeAttribute('href'); |
|
248 } |
|
249 return; |
|
250 } |
|
251 else |
|
252 { # only check for duplicate IDs on the implementation |
|
253 my $id= $node->getAttribute('id'); |
|
254 my $p = $node->getParentNode(); |
|
255 my $ptext = $p->getTagName()." \"".$p->getAttribute('id')."\""; |
|
256 if(defined $ID{$id}) |
|
257 { |
|
258 print STDERR "Error: duplicate ID: $tag \"$id\" in $ptext matches $ID{$id}\n"; |
|
259 } |
|
260 else |
|
261 { |
|
262 my $p = $node->getParentNode(); |
|
263 $ID{$id}="$tag in $ptext"; |
|
264 } |
|
265 } |
|
266 } |
|
267 elsif($tag=~/^(SystemDefinition|systemModel)$/ ) |
|
268 { |
|
269 } |
|
270 elsif($tag eq 'unit') |
|
271 { |
|
272 foreach my $atr ('bldFile','mrp','base','proFile') |
|
273 { |
|
274 my $link= $node->getAttribute($atr); |
|
275 if($link && !($link=~/^\//)) |
|
276 { |
|
277 $link= &abspath(File::Basename::dirname($file)."/$link"); |
|
278 foreach my $a (keys %rootmap) { |
|
279 $link=~s,^$a,$rootmap{$a},ie; |
|
280 } |
|
281 # remove leading ./ which is used to indicate that paths should remain relative |
|
282 $link=~s,^\./([^/]),$1,; |
|
283 $node->setAttribute($atr,$link); |
|
284 } |
|
285 } |
|
286 } |
|
287 elsif($tag eq 'meta') |
|
288 { |
|
289 my $rel= $node->getAttribute('rel') || 'Generic'; |
|
290 if($excludeMeta{$rel}) |
|
291 { |
|
292 $node->getParentNode->removeChild($node); |
|
293 return; |
|
294 } |
|
295 my $link= $node->getAttribute('href'); |
|
296 $link=~s,^file://(/([a-z]:/))?,$2,; # convert file URI to absolute path |
|
297 if ($link ne '' ) |
|
298 { |
|
299 if($link=~/^[\/]+:/) |
|
300 { |
|
301 print STDERR "Note: Remote URL $link not embedded\n"; |
|
302 next; # do not alter children |
|
303 } |
|
304 if(! ($link=~/^\//)) |
|
305 { |
|
306 $link= &abspath(File::Basename::dirname($file)."/$link"); |
|
307 } |
|
308 if(! -e $link) |
|
309 { |
|
310 print STDERR "Warning: Local metadata file not found: $link\n"; |
|
311 next; # do not alter children |
|
312 } |
|
313 # if we're here we can just embed the file |
|
314 # no processing logic is done! It's just embedded blindly |
|
315 my $item; |
|
316 eval { |
|
317 my $metadoc = $parser->parsefile ($link); |
|
318 $item = $metadoc->getDocumentElement; |
|
319 }; |
|
320 if(!$item) |
|
321 { |
|
322 print STDERR "Error: Could not process metadata file: $link\n"; |
|
323 next; # do not alter children |
|
324 } |
|
325 $node->removeAttribute('href'); |
|
326 &blindCopyInto($node,$item); |
|
327 } |
|
328 if($node->getAttribute('rel') eq 'link-mapping') |
|
329 {# need to process this now |
|
330 &rootMapMeta($node); |
|
331 } |
|
332 return; |
|
333 } |
|
334 else {return} |
|
335 my $checkversion=0; |
|
336 foreach my $item (@{$node->getChildNodes}) |
|
337 { |
|
338 #print $item->getNodeType,"\n"; |
|
339 &walk($file,$item); |
|
340 $checkversion = $checkversion || ($tag eq 'component' && $item->getNodeType==1 && $item->getAttribute('version') ne ''); |
|
341 } |
|
342 |
|
343 if($checkversion && scalar(@config)) |
|
344 { # need to check the conf metadata on the units in this component |
|
345 &doCmpConfig($node); |
|
346 } |
|
347 foreach my $item (@{$node->getChildNodes}) |
|
348 { |
|
349 if ($item->getNodeType==1 && $item->getTagName eq 'meta') |
|
350 { |
|
351 &processMeta($item); |
|
352 } |
|
353 } |
|
354 } |
|
355 |
|
356 |
|
357 sub combineLink |
|
358 { |
|
359 # combine data from linked sysdef fragment w/ equivalent element in parent document |
|
360 my $node = shift; |
|
361 my $file = shift; |
|
362 my $getfromfile = &localfile($file); |
|
363 $getfromfile eq '' && return; # already raised warning, no need to repeat |
|
364 my $doc = $parser->parsefile ($getfromfile); |
|
365 my $item =&firstElement($doc->getDocumentElement); |
|
366 $item || die "badly formatted $file"; |
|
367 &fixIDs($item); |
|
368 my %up = &atts($node); |
|
369 my %down = &atts($item); |
|
370 $up{'id'} eq $down{'id'} || die "$up{id} differs from $down{id}"; |
|
371 $node->removeAttribute('href'); |
|
372 foreach my $v (keys %up) {delete $down{$v}} |
|
373 foreach my $v (keys %down) |
|
374 { |
|
375 $node->setAttribute($v,$down{$v}) |
|
376 } |
|
377 foreach my $child (@{$item->getChildNodes}) |
|
378 { |
|
379 ©Into($node,$child); |
|
380 } |
|
381 &walk($file,$node); |
|
382 } |
|
383 |
|
384 |
|
385 sub blindCopyInto |
|
386 { |
|
387 # make a deep copy the node (2nd arg) into the element (1st arg) |
|
388 my $parent=shift; |
|
389 my $item = shift; |
|
390 my $doc = $parent->getOwnerDocument; |
|
391 my $type = $item->getNodeType; |
|
392 my $new; |
|
393 if($type==1) |
|
394 { |
|
395 $new = $doc->createElement($item->getTagName); |
|
396 my %down = &atts($item); |
|
397 while(my($a,$b) = each(%down)) |
|
398 { |
|
399 $new->setAttribute($a,$b); |
|
400 } |
|
401 foreach my $child (@{$item->getChildNodes}) |
|
402 { |
|
403 &blindCopyInto($new,$child); |
|
404 } |
|
405 } |
|
406 elsif($type==3) |
|
407 { |
|
408 $new = $doc->createTextNode ($item->getData); |
|
409 } |
|
410 elsif($type==8) |
|
411 { |
|
412 $new = $doc->createComment ($item->getData); |
|
413 } |
|
414 if($new) |
|
415 { |
|
416 $parent->appendChild($new); |
|
417 } |
|
418 } |
|
419 |
|
420 sub copyInto |
|
421 { |
|
422 # make a deep copy the node (2nd arg) into the element (1st arg) |
|
423 my $parent=shift; |
|
424 my $item = shift; |
|
425 my $doc = $parent->getOwnerDocument; |
|
426 my $type = $item->getNodeType; |
|
427 my $new; |
|
428 if($type==1) |
|
429 { |
|
430 &fixIDs($item); |
|
431 $new = $doc->createElement($item->getTagName); |
|
432 my %down = &atts($item); |
|
433 foreach my $ordered ('id','name','bldFile','mrp','level','levels','introduced','deprecated','filter') |
|
434 { |
|
435 if($down{$ordered}) |
|
436 { |
|
437 $new->setAttribute($ordered,$down{$ordered}); |
|
438 delete $down{$ordered} |
|
439 } |
|
440 } |
|
441 while(my($a,$b) = each(%down)) |
|
442 { |
|
443 $new->setAttribute($a,$b); |
|
444 } |
|
445 foreach my $child (@{$item->getChildNodes}) |
|
446 { |
|
447 ©Into($new,$child); |
|
448 } |
|
449 } |
|
450 elsif($type==3) |
|
451 { |
|
452 $new = $doc->createTextNode ($item->getData); |
|
453 } |
|
454 elsif($type==8) |
|
455 { |
|
456 $new = $doc->createComment ($item->getData); |
|
457 } |
|
458 if($new) |
|
459 { |
|
460 $parent->appendChild($new); |
|
461 } |
|
462 } |
|
463 |
|
464 sub getNs |
|
465 { |
|
466 # find the namespace URI that applies to the specified prefix. |
|
467 my $node = shift; |
|
468 my $pre = shift; |
|
469 my $uri = $node->getAttribute("xmlns:$pre"); |
|
470 if($uri) {return $uri} |
|
471 my $parent = $node->getParentNode; |
|
472 if($parent && $parent->getNodeType==1) |
|
473 { |
|
474 return getNs($parent,$pre); |
|
475 } |
|
476 } |
|
477 |
|
478 |
|
479 sub fixIDs |
|
480 { |
|
481 # translate the ID to use the root doc's namespaces |
|
482 my $node = shift; |
|
483 foreach my $id ('id','before') |
|
484 { |
|
485 &fixID($node,$id); |
|
486 } |
|
487 } |
|
488 |
|
489 sub fixID |
|
490 { |
|
491 # translate the ID to use the root doc's namespaces |
|
492 my $node = shift; |
|
493 my $attr = shift || 'id'; |
|
494 my $id = $node->getAttribute($attr); |
|
495 if($id eq '') {return} |
|
496 my $ns; |
|
497 if($id=~s/^(.*)://) |
|
498 { # it's got a ns, find out what it is |
|
499 my $pre = $1; |
|
500 $ns=&getNs($node,$pre); |
|
501 } |
|
502 else |
|
503 { |
|
504 $ns = $node->getOwnerDocument->getDocumentElement->getAttribute("id-namespace") || |
|
505 $defaultns; |
|
506 } |
|
507 $ns = $urimap{$ns}; |
|
508 $id = ($ns eq '') ? $id : "$ns:$id"; |
|
509 return $node->setAttribute($attr,$id); |
|
510 } |
|
511 |
|
512 sub firstElement { |
|
513 # return the first element in this node |
|
514 my $node = shift; |
|
515 foreach my $item (@{$node->getChildNodes}) { |
|
516 if($item->getNodeType==1) {return $item} |
|
517 } |
|
518 } |
|
519 |
|
520 |
|
521 sub atts { |
|
522 # return a hash of all attribtues defined for this element |
|
523 my $node = shift; |
|
524 my %at = $node->getAttributes; |
|
525 my %list; |
|
526 foreach my $a (keys %{$node->getAttributes}) |
|
527 { |
|
528 if($a ne '') |
|
529 { |
|
530 $list{$a} = $node->getAttribute ($a); |
|
531 } |
|
532 } |
|
533 return %list; |
|
534 } |
|
535 |
|
536 |
|
537 sub ns |
|
538 { |
|
539 # return a hash of ns prefix and uri -- the xmlns: part is stripped off |
|
540 my $node = shift; |
|
541 my %list; |
|
542 foreach my $a (keys %{$node->getAttributes}) |
|
543 { |
|
544 my $pre = $a; |
|
545 if($pre=~s/^xmlns://) |
|
546 { |
|
547 $list{$pre} = $node->getAttribute ($a); |
|
548 } |
|
549 } |
|
550 return %list; |
|
551 } |
|
552 |
|
553 |
|
554 sub resolvePath |
|
555 { |
|
556 # return full path to 2nd arg relative to first (path or absolute URI) |
|
557 my $base = shift; |
|
558 my $path = shift; |
|
559 if($path=~m,^/,) {return $path } # path is absolute, but has no drive. Let OS deal with it. |
|
560 if($path=~s,^file:///([a-zA-Z]:/),$1,) {return $path } # file URI with drive letter |
|
561 if($path=~m,^file://,) {return $path } # file URI with no drive letter (unit-style). Just pass on as is with leading / and let OS deal with it |
|
562 if($path=~m,^[a-z0-9][a-z0-9]+:,i) {return $path } # absolute URI -- no idea how to handle, so just return |
|
563 return &abspath(File::Basename::dirname($base)."/$path"); |
|
564 } |
|
565 |
|
566 |
|
567 sub resolveURI |
|
568 { |
|
569 # return full path to 2nd arg relative to first (path or absolute URI) |
|
570 my $base = shift; |
|
571 my $path = shift; |
|
572 if($path=~m,[a-z0-9][a-z0-9]+:,i) {return $path } # absolute URI -- just return |
|
573 if($path=~m,^/,) {return $path } # path is absolute, but has no drive. Let OS deal with it. |
|
574 return &abspath(File::Basename::dirname($base)."/$path"); |
|
575 } |
|
576 |
|
577 sub localfile |
|
578 { |
|
579 my $file = shift; |
|
580 if($file=~s,file:///([a-zA-Z]:/),$1,) {return $file } # file URI with drive letter |
|
581 if($file=~m,file://,) {return $file } # file URI with no drive letter (unit-style). Just pass on as is with leading / and let OS deal with it |
|
582 if($file=~m,^([a-z0-9][a-z0-9]+):,i) |
|
583 { |
|
584 print STDERR "ERROR: $1 scheme not supported\n"; |
|
585 return; # return empty string if not supported. |
|
586 } |
|
587 return $file |
|
588 } |
|
589 |
|
590 sub namespaces |
|
591 { |
|
592 # return a list of namespace URI / prefix pairs, in the order they're defined |
|
593 # these need to be used to define namespaces in the root element |
|
594 my $file = shift; |
|
595 my $node = shift; |
|
596 my $type = $node->getNodeType; |
|
597 if($type!=1) {return} |
|
598 my $tag = $node->getTagName; |
|
599 my @res; |
|
600 my %nslist = &ns($node); |
|
601 while(my($pre,$uri)=each(%nslist)) |
|
602 { # push all namespaces defined here onto the list |
|
603 push(@res,$uri,$pre); |
|
604 } |
|
605 if($tag=~/^(layer|package|collection|component)$/ ) |
|
606 { # these have the potential of linking, so check for that |
|
607 my $link= $node->getAttribute('href'); |
|
608 if($link) |
|
609 { |
|
610 $link=&resolvePath($file,$link); |
|
611 if(-e $link) |
|
612 { |
|
613 my $doc; |
|
614 eval { |
|
615 $doc = $parser->parsefile ($link); |
|
616 }; |
|
617 if($doc) |
|
618 { |
|
619 &checkSyntaxVersion($doc->getDocumentElement->getAttribute('schema')); # ensure we track we highest syntax number |
|
620 my @docns = &namespaces($link,$doc->getDocumentElement); |
|
621 undef $doc; |
|
622 return (@res,@docns); |
|
623 #ignore any children nodes if this is a link |
|
624 } |
|
625 print STDERR "Error: Malformed XML. Could not process $link\n"; |
|
626 } |
|
627 # print STDERR "Note: $link not found\n"; -- no need to warm now. Do so later when trying to join |
|
628 } |
|
629 } |
|
630 elsif($tag eq 'SystemDefinition' ) |
|
631 { |
|
632 my $default = $node->getAttribute('id-namespace'); |
|
633 if($default) |
|
634 {# mangle with a space so it's clear it's not a qname |
|
635 push(@res,$default,'id namespace'); |
|
636 } |
|
637 } |
|
638 foreach my $item (@{$node->getChildNodes}) |
|
639 { |
|
640 push(@res,&namespaces($file,$item)); |
|
641 } |
|
642 return @res; |
|
643 } |
|
644 |
|
645 sub processMeta |
|
646 { # acts upon any known <meta> and strips it from the output if it's used |
|
647 my $metanode = shift; |
|
648 |
|
649 my $rel = $metanode->getAttribute('rel') || 'Generic'; |
|
650 if($rel eq 'config' && scalar(@config)) |
|
651 { # only process if there is something to configure |
|
652 &doconfig($metanode); |
|
653 } |
|
654 else |
|
655 { |
|
656 # do nothing. Not supported yet |
|
657 } |
|
658 } |
|
659 |
|
660 sub doCmpConfig |
|
661 { # configure in or out the units in a component |
|
662 my $cmp = shift; # the component node |
|
663 my @unversioned; # list of all units with no version attribute (if more than one, they should all have filters defined) |
|
664 my %versioned; # hash table of all units with a specified version, it's a fatal error to hav the same verison twice in one component |
|
665 foreach my $item (@{$cmp->getChildNodes}) |
|
666 { # populate %versioned and @unversioned to save processsing later |
|
667 if($item->getNodeType==1 && $item->getTagName eq 'unit') |
|
668 { |
|
669 my $ver = $item->getAttribute('version'); |
|
670 if($ver eq '') {push(@unversioned,$item)} |
|
671 else |
|
672 { |
|
673 defined $versioned{$ver} && die "Cannot have more than one unit with version $ver in the same component ".$cmp->getAttribute('id'); |
|
674 $versioned{$ver}=$item; |
|
675 } |
|
676 } |
|
677 } |
|
678 my @picks = &getMetaConfigPick($cmp); # the list, in order, of all <pick> elements that affect this component |
|
679 foreach my $pick (@picks) |
|
680 { |
|
681 my $ver = $pick->getAttribute('version'); |
|
682 if(!$versioned{$ver}) |
|
683 { |
|
684 print STDERR "ERROR: Reference to invalid unit version $ver in component ",$cmp->getAttribute('id'),". Ignoring.\n"; |
|
685 return; |
|
686 } |
|
687 if(&definedMatches($pick)) |
|
688 { # remove all other units; |
|
689 delete $versioned{$ver}; # to avoid removing in loop |
|
690 foreach my $unit (@unversioned, values(%versioned)) |
|
691 { |
|
692 $cmp->removeChild($unit); |
|
693 print STDERR "Note: unit ",$unit->getAttribute('version')," in component " ,$cmp->getAttribute('id')," configured out\n"; |
|
694 } |
|
695 last; # done. No more processing after first match |
|
696 } |
|
697 else |
|
698 { # remove this unit and continue |
|
699 $cmp->removeChild($versioned{$ver}); |
|
700 print STDERR "Note: unit $ver in component " ,$cmp->getAttribute('id')," configured out\n"; |
|
701 delete $versioned{$ver}; # gone, don't process anymore; |
|
702 } |
|
703 } |
|
704 if (scalar(@unversioned, values(%versioned)) > 1) |
|
705 { |
|
706 print STDERR "Warning: component ",$cmp->getAttribute('id')," has more than one unit after configuration\n"; |
|
707 } |
|
708 } |
|
709 |
|
710 |
|
711 sub getMetaConfigPick |
|
712 { # return an array of all <pick> elements that affect the specified element |
|
713 my $node = shift; |
|
714 my @pick; |
|
715 while($node->getParentNode->getNodeType==1) |
|
716 { |
|
717 foreach my $item (@{$node->getChildNodes}) |
|
718 { |
|
719 my @picks; |
|
720 if($item->getNodeType==1 && $item->getAttribute('rel') eq 'config') |
|
721 { # it's conf metadata |
|
722 foreach my $p (@{$item->getChildNodes}) |
|
723 { |
|
724 if($p->getNodeType==1 && $p->getTagName eq 'pick') {push(@picks,$p)} |
|
725 } |
|
726 } |
|
727 @pick=(@picks,@pick); # prepend this to the start; |
|
728 } |
|
729 $node=$node->getParentNode; |
|
730 } |
|
731 return @pick; |
|
732 } |
|
733 |
|
734 sub definedMatches |
|
735 { # process all <defined> and <not-defined> the specified element and return true or false if the combination matches |
|
736 my $node = shift; |
|
737 my $match = 1; |
|
738 foreach my $def (@{$node->getChildNodes}) |
|
739 { |
|
740 if($def->getNodeType == 1) |
|
741 { |
|
742 my $tag = $def->getTagName; |
|
743 if($tag eq 'defined' or $tag eq 'not-defined') |
|
744 { |
|
745 my $var = $def->getAttribute('condition') || die "Must have condition set on all $tag elements"; |
|
746 $defineParams{$var} && die "Cannot use a macro with parameters as a feature flag: $var(".$defineParams{$var}->[0].")"; |
|
747 $match = $match && (($tag eq 'defined') ? defined($defines{$var}) : ! defined($defines{$var})); |
|
748 } |
|
749 } |
|
750 } |
|
751 return $match; |
|
752 } |
|
753 |
|
754 sub doconfig |
|
755 { # confgure in or out a system model item that owns the specified <meta>, remove the <meta> when done. |
|
756 my $meta = shift; |
|
757 my $keep = definedMatches($meta); |
|
758 my $parent = $meta->getParentNode; |
|
759 if(!$keep) |
|
760 { |
|
761 print STDERR "Note: ",$parent->getTagName," " ,$parent->getAttribute('id')," configured out\n"; |
|
762 $parent->getParentNode->removeChild($parent); |
|
763 return; # it's removed, so there's nothing else we can possibly do |
|
764 } |
|
765 |
|
766 $parent->removeChild($meta); |
|
767 } |
|
768 |
|
769 sub getDefines |
|
770 { # populate the list of #defines from a specified .hrh file. |
|
771 my $file = shift; |
|
772 my $inc; |
|
773 foreach my $i (@includes) |
|
774 { |
|
775 $inc.=" -I$i"; |
|
776 } |
|
777 open(CPP,"cpp -dD$inc \"$file\"|"); |
|
778 while(<CPP>) |
|
779 { |
|
780 if(!/\S/){next} # skip blank lines |
|
781 if(/^# [0-9]+ /) {next} # don't care about these |
|
782 s/\s+$//; |
|
783 if(s/^#define\s+(\S+)\((.*?)\)\s+//) |
|
784 { #parametered define |
|
785 push(@{$defineParams{$1}},@2,$_); |
|
786 } |
|
787 elsif(s/^#define\s+(\S+)//) |
|
788 { # normal define |
|
789 my $def = $1; |
|
790 s/^\s+//; |
|
791 $defines{$1}=$_; |
|
792 } |
|
793 else {die "cannot process $_";} |
|
794 } |
|
795 close CPP; |
|
796 $? && die "Call to cpp produced an error"; |
|
797 } |
|
798 |
|
799 sub checkSyntaxVersion |
|
800 { # check if supplied version number is greater than $maxschema |
|
801 my $schema = shift; |
|
802 my @max=split(/\./,$maxschema); |
|
803 my @cur=split(/\./,$schema); |
|
804 while(@max) |
|
805 { |
|
806 ($max[0] > $cur[0]) && return; # max is bigger, do nothing |
|
807 if($cur[0] > $max[0]) |
|
808 { |
|
809 $maxschema=$schema; |
|
810 return; |
|
811 } |
|
812 shift @max; |
|
813 shift @cur; |
|
814 } |
|
815 # they are equal - do nothing |
|
816 } |
|
817 |
|
818 sub help |
|
819 { |
|
820 my $name= $0; $name=~s,^.*[\\/],,; |
|
821 my $text; |
|
822 format STDERR = |
|
823 ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
|
824 $text, |
|
825 ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ |
|
826 $text |
|
827 . |
|
828 print STDERR "usage: $name [options...] sysdef\n valid options are:\n\n"; |
|
829 foreach ( |
|
830 "-path\tspecifies the full system-model path to the file which is being processed. By default this is \"/os/deviceplatformrelease/foundation_system/system_model/system_definition.xml\"", |
|
831 " This must be an absolute path if you're processing a root sysdef.", |
|
832 " If processing a pkgdef file, you can use \"./package_definition.xml\" to leave all links relative.", |
|
833 |
|
834 "-output\tspecifies the file to save the output to. If not specified this will write to stdout", |
|
835 |
|
836 "-config\tspecifies the name of an .hrh file in which the configuration data is acquired from. If not set, no confguration will be done.", |
|
837 " If it is set, all configuration metadata will be processed and stripped from the output, even if the confguration data is empty", |
|
838 "-I[path]\tspecifies the include paths to use when resolving #includes in the .hrh file. This uses the same syntax as cpp command uses: a captial \"I\" followed by the path with no space in between. Any number of these can be provided.", |
|
839 "-exclude-meta [rel]\tspecifies the 'rel' value of <meta> elements to exclude from the output. Any number of these can be provided. The following meta rel values affect the processing of the system definition and cannot be excluded: ".join(', ',@cannotExclude) |
|
840 ) { |
|
841 $text = $_; |
|
842 write STDERR; |
|
843 print STDERR "\n"; |
|
844 } |
|
845 |
|
846 exit(1); |
|
847 } |
|
848 |
|
849 |
|
850 |