diff -r 96fee2635b19 -r f70b728ea30c metatools/sysdeftools/checklinks.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/metatools/sysdeftools/checklinks.pl Wed Jul 28 13:20:46 2010 +0100 @@ -0,0 +1,525 @@ +# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# This component and the accompanying materials are made available +# under the terms of "Eclipse Public License v1.0" +# which accompanies this distribution, and is available +# at the URL "http://www.eclipse.org/legal/epl-v10.html". +# +# Initial Contributors: +# Nokia Corporation - initial contribution. +# +# Contributors: +# +# Description: +# Script to validate the unit links in a system definition or package definition XML file +#!/usr/bin/perl + +use strict; + + +use FindBin; # for FindBin::Bin +use lib $FindBin::Bin; +use lib "$FindBin::Bin/lib"; + +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Spec; +use XML::DOM; + +my $output; +my $path; +my %defineParams; +my %defines; +my $defaultns = 'http://www.symbian.org/system-definition'; # needed if no DTD +my $realloc; + +# need to add options for controlling which metas are filtered out and which are included inline +GetOptions + ( + 'path=s' => $path, + 'effective-sysdef=s' => \$realloc + ); + +# -path specifies the full system-model path to the file which is being processed. +# This must be an absolute path if you're processing a root sysdef. +# 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. + + +# if config is not set, no confguration will be done. +# If it is set, all configuration metadata will be processed and stripped from the output, even if the confguration data is empty + + if($path eq '') {$path = '/os/deviceplatformrelease/foundation_system/system_model/system_definition.xml'} + +($#ARGV == -1 ) && &help(); +my $sysdef = &abspath(shift); # resolve the location of the root sysdef + +$realloc = $realloc || $sysdef; + +my %unitmap; +my @p1=reverse(split(/[\\\/]/,$path)); +my @p2=reverse(split(/[\\\/]/,$realloc)); + +shift(@p1);shift(@p2); # don't care abt file name +while(lc($p1[0]) eq lc($p2[0])) {shift(@p1);shift(@p2)} + +$unitmap{join('/',reverse(@p1))} = join("/",reverse(@p2)); + +my @p1=reverse(split(/[\\\/]/,$sysdef)); +my @p2=reverse(split(/[\\\/]/,$realloc)); + +shift(@p1);shift(@p2); # don't care abt file name +while(lc($p1[0]) eq lc($p2[0]) && scalar(@p1)) {shift(@p1);shift(@p2)} + +$unitmap{join('/',reverse(@p1))} = join("/",reverse(@p2)); + + +# rootmap is a mapping from the filesystem to the paths in the doc +my %rootmap = &rootMap($path,$sysdef); +my %nsmap; +my %urimap; + +my $parser = new XML::DOM::Parser; +my $sysdefdoc; +eval { + $sysdefdoc = $parser->parsefile ($sysdef); +}; +if(!$sysdefdoc) { + die "ERROR: could not open $sysdef\n"; +} + + + +my $maxschema = $sysdefdoc->getDocumentElement()->getAttribute('schema'); # don't check value, just store it. + +my $docroot = $sysdefdoc->getDocumentElement; + +my $ns = $docroot->getAttribute('id-namespace'); +if(!$ns && $nsmap{''}) + { + $docroot->setAttribute('id-namespace',$nsmap{''}); + } + +$docroot->setAttribute('schema',$maxschema); # output has the largest syntax version of all includes + + +while(my($pre,$uri) = each(%nsmap)) + { + $pre ne '' || next ; + $docroot->setAttribute("xmlns:$pre",$uri); + } + +&walk($sysdef,$docroot); # process the XML + + +sub abspath + { # normalize the path into an absolute one + my ($name,$path) = fileparse($_[0]); + $path=~tr,\\,/,; + if( -e $path) + { + return abs_path($path)."/$name"; + } + my @dir = split('/',$_[0]); + my @new; + foreach my $d (@dir) + { + if($d eq '.') {next} + if($d eq '..') + { + pop(@new); + next; + } + push(@new,$d) + } + return join('/',@new); + } + +sub rootMap { + my @pathdirs = split(/\//,$_[0]); + my @rootdirs = split(/\//,$_[1]); + + while(lc($rootdirs[$#rootdirs]) eq lc($pathdirs[$#pathdirs]) ) + { + pop(@rootdirs); + pop(@pathdirs); + } + return (join('/',@rootdirs) => join('/',@pathdirs) ); + } + +sub rootMapMeta { + # find all the explict path mapping from the link-mapping metadata + my $node = shift; + foreach my $child (@{$node->getChildNodes}) + { + if ($child->getNodeType==1 && $child->getTagName eq 'map-prefix') + { + my $from = $child->getAttribute('link'); + my $to = $child->getAttribute('to'); # optional, but blank if not set + $rootmap{$from} = $to; + } + } + # once this is processed we have no more need for it. Remove from output + $node->getParentNode->removeChild($node); + } + + +sub walk + { # walk through the doc, resolving all links + my $file = shift; + my $node = shift; + my $type = $node->getNodeType; + if($type!=1) {return} + my $tag = $node->getTagName; + if($tag=~/^(layer|package|collection|component)$/ ) + { + my $link= $node->getAttribute('href'); + if($link) + { + my $file = &resolvePath($file,$link); + if(-e $file) + { + &combineLink($node,$file); + } + else + { + print "Note: $file not found\n"; + $node->removeAttribute('href'); + } + return; + } + } + elsif($tag=~/^(SystemDefinition|systemModel)$/ ) + { + } + elsif($tag eq 'unit') + { + my %at = &atts($node); + my $pro; + foreach my $o (keys(%at)) + { + if($o eq 'proFile' || $o=~/:proFile$/) + { + $pro = $at{$o}; + last; + } + } + my $filter=$node->getParentNode()->getAttribute('filter'); + if($filter ne '' && $at{'filter'}) {$filter.=','.$at{'filter'}} + elsif($at{'filter'}) {$filter=$at{'filter'}} + if($filter ne '') {$filter="\t($filter)"} + foreach my $atr ('bldFile','mrp','base') + { + my $ext; + my $link= $at{$atr}; + if($atr eq 'bldFile') { + $ext = ($pro ne '') ? "/$pro" : '/bld.inf' + } + if($link ne '') + { + my $ok = 0; + my $trylink; + if($link && !($link=~/^\//)) + { + $link= &abspath(File::Basename::dirname($file)."/$link"); + $ok = (-e "$link$ext"); + if(!$ok) + { + foreach my $a (keys %rootmap) + { + $link=~s,^$a,$rootmap{$a},ie; + # remove leading ./ which is used to indicate that paths should remain relative + $link=~s,^\./([^/]),$1,; + } + + } + } + if(!$ok) + { + foreach my $a (keys %unitmap) { + if($a eq substr($link,0,length($a))) { + $trylink = $unitmap{$a}.substr($link,length($a)); + if(-e "$trylink$ext") { + $ok=1; + $link = $trylink; + last; + } + } + } + } + if(!$ok) + { + print "Error: $atr not found in ",($trylink ne '') ? $trylink : $link,"$filter\n"; + } + } + } + } + elsif($tag eq 'meta') + { + my $rel= $node->getAttribute('rel') || 'Generic'; + my $link= $node->getAttribute('href'); + $link=~s,^file://(/([a-z]:/))?,$2,; # convert file URI to absolute path + if ($link ne '' ) + { + if($link=~/^[\/]+:/) + { + print "Note: Remote URL $link not validated\n"; + next; # do not alter children + } + if(! ($link=~/^\//)) + { + $link= &abspath(File::Basename::dirname($file)."/$link"); + } + if(! -e $link) + { + if(! -e &realPath($link)) { + print "Warning: Local metadata file not found: $link\n"; + } + next; # do not alter children + } + } + if($node->getAttribute('rel') eq 'link-mapping') + {# need to process this now + &rootMapMeta($node); + } + return; + } + else {return} + my $checkversion=0; + foreach my $item (@{$node->getChildNodes}) + { + #print $item->getNodeType,"\n"; + &walk($file,$item); + } + + + + } + + +sub realPath + { + my $link = shift; + foreach my $a (keys %unitmap) + { + if($a eq substr($link,0,length($a))) + { + my $trylink = $unitmap{$a}.substr($link,length($a)); + if(-e $trylink) {return $trylink} + } + } + } + +sub combineLink + { + # combine data from linked sysdef fragment w/ equivalent element in parent document + my $node = shift; + my $file = shift; + my $getfromfile = &localfile($file); + $getfromfile eq '' && return; # already raised warning, no need to repeat + my $doc; + eval { + $doc = $parser->parsefile ($getfromfile); + }; + if(!$doc) { + print "ERROR: could not open $getfromfile\n"; + return; + } + my $item =&firstElement($doc->getDocumentElement); + $item || die "badly formatted $file"; + my @upid = &getNamespaceAndValue($node,'id'); + my @downid = &getNamespaceAndValue($item,'id'); + (($upid[0] eq $downid[0]) && ($upid[1] eq $downid[1])) || die "$upid[1] ($upid[0]) differs from $downid[1] ($downid[0]) "; # make sure the link is valid + &walk($getfromfile,$item); + } + + +sub copyInto + { + # make a deep copy the node (2nd arg) into the element (1st arg) + my $parent=shift; + my $item = shift; + my $doc = $parent->getOwnerDocument; + my $type = $item->getNodeType; + my $new; + if($type==1) + { + $new = $doc->createElement($item->getTagName); + my %down = &atts($item); + foreach my $ordered ('id','name','bldFile','mrp','level','levels','introduced','deprecated','filter') + { + if($down{$ordered}) + { + $new->setAttribute($ordered,$down{$ordered}); + delete $down{$ordered} + } + } + while(my($a,$b) = each(%down)) + { + $new->setAttribute($a,$b); + } + foreach my $child (@{$item->getChildNodes}) + { + ©Into($new,$child); + } + } + elsif($type==3) + { + $new = $doc->createTextNode ($item->getData); + } + elsif($type==8) + { + $new = $doc->createComment ($item->getData); + } + if($new) + { + $parent->appendChild($new); + } + } + + +sub getNamespaceAndValue + { + my $node = shift; + my $attr = shift || 'id'; + my $id = $node->getAttribute($attr); + if($id eq '') {return} + my $ns; + if($id=~s/^(.*)://) + { # it's got a ns, find out what it is + $ns=&getNs($node,$1); + } + else + { + $ns = $node->getOwnerDocument->getDocumentElement->getAttribute("id-namespace") || + $defaultns; + } + return ($ns,$id);; + } + +sub getNs + { + # find the namespace URI that applies to the specified prefix. + my $node = shift; + my $pre = shift; + my $uri = $node->getAttribute("xmlns:$pre"); + if($uri) {return $uri} + my $parent = $node->getParentNode; + if($parent && $parent->getNodeType==1) + { + return getNs($parent,$pre); + } + } + + + +sub firstElement { + # return the first element in this node + my $node = shift; + foreach my $item (@{$node->getChildNodes}) { + if($item->getNodeType==1) {return $item} + } +} + + +sub atts { + # return a hash of all attribtues defined for this element + my $node = shift; + my %at = $node->getAttributes; + my %list; + foreach my $a (keys %{$node->getAttributes}) + { + if($a ne '') + { + $list{$a} = $node->getAttribute ($a); + } + } + return %list; +} + + +sub ns + { + # return a hash of ns prefix and uri -- the xmlns: part is stripped off + my $node = shift; + my %list; + foreach my $a (keys %{$node->getAttributes}) + { + my $pre = $a; + if($pre=~s/^xmlns://) + { + $list{$pre} = $node->getAttribute ($a); + } + } + return %list; + } + + +sub resolvePath + { + # return full path to 2nd arg relative to first (path or absolute URI) + my $base = shift; + my $path = shift; + if($path=~m,^/,) {return $path } # path is absolute, but has no drive. Let OS deal with it. + if($path=~s,^file:///([a-zA-Z]:/),$1,) {return $path } # file URI with drive letter + 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 + if($path=~m,^[a-z0-9][a-z0-9]+:,i) {return $path } # absolute URI -- no idea how to handle, so just return + return &abspath(File::Basename::dirname($base)."/$path"); + } + + +sub resolveURI + { + # return full path to 2nd arg relative to first (path or absolute URI) + my $base = shift; + my $path = shift; + if($path=~m,[a-z0-9][a-z0-9]+:,i) {return $path } # absolute URI -- just return + if($path=~m,^/,) {return $path } # path is absolute, but has no drive. Let OS deal with it. + return &abspath(File::Basename::dirname($base)."/$path"); + } + +sub localfile + { + my $file = shift; + if($file=~s,file:///([a-zA-Z]:/),$1,) {return $file } # file URI with drive letter + 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 + if($file=~m,^([a-z0-9][a-z0-9]+):,i) + { + print "ERROR: $1 scheme not supported\n"; + return; # return empty string if not supported. + } + return $file + } + + + + + +sub help + { + my $name= $0; $name=~s,^.*[\\/],,; +my $text; +format STDERR = + ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + $text, + ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ + $text +. +print STDERR "usage: $name [options...] sysdef\n valid options are:\n\n"; + foreach ( + "-path [sm-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\"", + " This must be an absolute path if you're processing a root sysdef.", + " If processing a pkgdef file, you can use \"./package_definition.xml\" to leave all links relative.", + "effective-sysdef [local-file]\tspecifies another local filesystem location the sysdef should be considered when resolving linked metas and unit paths, but not system model item hrefs. This is mainly used for testing system-wide changes to pkgdefs since it allows the pkgdefs to exist in a separate location to the rest of the codeline" + ) { + $text = $_; + write STDERR; + print STDERR "\n"; + } + + exit(1); + } + + +