bldsystemtools/commonbldutils/buildenv.pm
changeset 0 83f4b4db085c
child 1 d4b442d23379
equal deleted inserted replaced
-1:000000000000 0:83f4b4db085c
       
     1 # Copyright (c) 2004-2009 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 # This module implements Build environment logging
       
    15 # It collects versions of various tools and Windows hotfixes and writes them to the specified XML file
       
    16 # 
       
    17 #
       
    18 
       
    19 package buildenv;
       
    20 
       
    21 use strict;
       
    22 use Carp;
       
    23 use lib "$FindBin::Bin/lib";
       
    24 
       
    25 # Module Win32::TieRegistry - Set delimiter to forward slash to avoid doubling all the backslashes!
       
    26 # Do not ask for ArrayValues sice we are only reading data, not changing/editing anything.
       
    27 use Win32::TieRegistry( Delimiter=>"/" );
       
    28 
       
    29 # Main
       
    30 #
       
    31 # Inputs
       
    32 # - $iHostName        - Name of host computer (to be written into the XML file)
       
    33 # - $iXMLfilePathname - Full pathname of output .XML file
       
    34 #
       
    35 #   note: XML file will have been named after the hostname that
       
    36 #         the script was run on. See BuildEnv.pl.
       
    37 #
       
    38 # Description
       
    39 #   Collects OS information and versions of know tools.
       
    40 #   Writes resulting build environment info to specified XML file
       
    41 #
       
    42 sub Main
       
    43 {
       
    44   my ($iHostName, $iXMLfilePathname)      = @_;
       
    45   my (%iToolList)                = &buildenv::GetToolInfo();
       
    46   my ($iWinEnvVer, %iHotFixList) = &buildenv::GetWinInfo();
       
    47   
       
    48   # write the same info in xml - useful for future tools
       
    49   &WriteXMLFormat($iHostName, $iXMLfilePathname, $iWinEnvVer, \%iHotFixList, \%iToolList);
       
    50 }
       
    51 
       
    52 # WriteXMLFormat
       
    53 #
       
    54 # Description
       
    55 #   Writes build environment info to XML file
       
    56 #
       
    57 # Inputs
       
    58 #   - $iWinVer      - scalar with info on windows version
       
    59 #   - $iHotFixRef   - ref to hash containing hotfix info
       
    60 #   - $iToolListRef - ref to hash containing tool info
       
    61 #
       
    62 # Outputs
       
    63 #   Writes XML file
       
    64 #
       
    65 sub WriteXMLFormat
       
    66 {
       
    67   use IO;
       
    68   use XML::Writer;
       
    69   
       
    70   # get one scalar and 2 refs to hashes
       
    71   my ($iHostName, $iXMLfilePathname, $iWinVer, $iHotFixListRef, $iToolListRef) = @_;
       
    72   
       
    73   my $DTD = "
       
    74     <!DOCTYPE machine_config [
       
    75     <!ELEMENT machine_config (operating_sys,tool*)>
       
    76     <!ATTLIST machine_config
       
    77       name CDATA #REQUIRED
       
    78     >
       
    79     <!ELEMENT operating_sys (hotfix*)>
       
    80     <!ATTLIST operating_sys
       
    81       name         CDATA #REQUIRED
       
    82       version      CDATA #REQUIRED
       
    83       servicepack  CDATA #REQUIRED
       
    84       buildnumber  CDATA #REQUIRED
       
    85     >
       
    86     <!ELEMENT hotfix EMPTY>
       
    87     <!ATTLIST hotfix
       
    88       name         CDATA #REQUIRED
       
    89       installdate  CDATA #REQUIRED
       
    90     >
       
    91     <!ELEMENT tool EMPTY>
       
    92     <!ATTLIST tool
       
    93       name      CDATA #REQUIRED
       
    94       version   CDATA #REQUIRED
       
    95     >
       
    96     ]> ";
       
    97   
       
    98   my $output = new IO::File("> $iXMLfilePathname");
       
    99   my $writer = new XML::Writer( OUTPUT => $output, DATA_MODE => 'true', DATA_INDENT => 2  );
       
   100   
       
   101   $writer->xmlDecl( 'UTF-8' );
       
   102   print $output $DTD;
       
   103   $writer->comment( 'machine_config  at ' . localtime() );
       
   104   $writer->startTag( 'machine_config', 'name' => $iHostName);
       
   105   
       
   106       # breakdown the winversion string to its component parts:
       
   107       $iWinVer =~ m/Microsoft Windows(.*)ver(.*)Service Pack(.*)Build(.*)/;
       
   108       $writer->startTag( 'operating_sys', 'name'       => 'Microsoft Windows'.$1,
       
   109                                           'version'    => $2,
       
   110                                           'servicepack'=> $3,
       
   111                                           'buildnumber'=> $4);
       
   112           
       
   113       foreach my $fixnum (sort keys %$iHotFixListRef)
       
   114       {
       
   115         $writer->startTag( 'hotfix', name => $fixnum, 'installdate' => $iHotFixListRef->{$fixnum} );
       
   116         $writer->endTag( );
       
   117       }
       
   118       $writer->endTag( ); # operating_sys
       
   119       foreach my $toolname (sort {uc $a cmp uc $b} keys %$iToolListRef)
       
   120       {
       
   121         $writer->startTag( 'tool', name => $toolname, 'version' => $iToolListRef->{$toolname}{'version'} );
       
   122         # Look for modules supporting the current tool (e.g Perl modules)
       
   123         if (defined $iToolListRef->{$toolname}{'modules'})
       
   124         {
       
   125             foreach my $modulename (sort {uc $a cmp uc $b} keys %{$iToolListRef->{$toolname}{'modules'}})
       
   126             {
       
   127                 $writer->startTag( 'module', name => $modulename, 'version' => $iToolListRef->{$toolname}{'modules'}{$modulename} );
       
   128                 $writer->endTag( );
       
   129             }
       
   130         }
       
   131         # Look for other versions of the current tool for which files exist but are not reached via default PATH (e.g ARM RVCT)
       
   132         if (defined $iToolListRef->{$toolname}{'multiver'})
       
   133         {
       
   134             foreach my $multiverdirectory (sort {uc $a cmp uc $b} keys %{$iToolListRef->{$toolname}{'multiver'}})
       
   135             {
       
   136                 $writer->startTag( 'multiversion', name => $multiverdirectory, 'version' => $iToolListRef->{$toolname}{'multiver'}{$multiverdirectory} );
       
   137                 $writer->endTag( );
       
   138             }
       
   139         }
       
   140         $writer->endTag( );
       
   141       }
       
   142   $writer->endTag( );     # machine_config
       
   143   $writer->end( );
       
   144 }
       
   145 
       
   146 # GetWinInfo
       
   147 #
       
   148 # Description
       
   149 #   Gets Windows version. Collects information on Windows hotfixes (patches)
       
   150 #
       
   151 # Inputs - None
       
   152 #
       
   153 # Returns
       
   154 #   $iWinEnv - Windows version, SP# and build
       
   155 #   %iHotFixList - Installed Hotfix patch installation dates
       
   156 #
       
   157 sub GetWinInfo
       
   158 {
       
   159   
       
   160     my %iHotFixList;
       
   161     my $iWinEnv = 'Windows : Unknown version';
       
   162 
       
   163     # Extract information from the Windows Registry - First get the OS name and version
       
   164     my %iValues;
       
   165     my $iRegKey   = 'LMachine/SOFTWARE/Microsoft/Windows NT/CurrentVersion';
       
   166     # Get data from hash set up by Win32::TieRegistry
       
   167     my $iHashRef = $Registry->{$iRegKey} or  return ($iWinEnv, %iHotFixList);
       
   168     # Check that hash element exists before referencing data. Otherwise TieRegistry will think that we want to create a new key/value
       
   169     my $iProd = (defined $iHashRef->{'/ProductName'})? $iHashRef->{'/ProductName'}: '';
       
   170     my $iVer = (defined $iHashRef->{'/CurrentVersion'})? $iHashRef->{'/CurrentVersion'}: '';
       
   171     my $iSPVer = (defined $iHashRef->{'/CSDVersion'})? $iHashRef->{'/CSDVersion'}: '';
       
   172     my $iBuild = (defined $iHashRef->{'/CurrentBuildNumber'})? $iHashRef->{'/CurrentBuildNumber'}: '';
       
   173 
       
   174     $iWinEnv =$iProd .' ver ' . $iVer . ' ' . $iSPVer . ' Build ' . $iBuild . "\n";
       
   175 
       
   176     # Next get the list of patches - First assume "Windows 2003" then "Windows 2000"
       
   177     $iRegKey   = 'LMachine/SOFTWARE/Microsoft/Updates/Windows Server 2003';
       
   178     $iHashRef = $Registry->{$iRegKey};
       
   179     unless (defined $iHashRef)
       
   180     {
       
   181         $iRegKey   = 'LMachine/SOFTWARE/Microsoft/Updates/Windows 2000';
       
   182         $iHashRef = $Registry->{$iRegKey};
       
   183         unless (defined $iHashRef)
       
   184         {
       
   185             return ($iWinEnv, %iHotFixList);
       
   186         }
       
   187     }
       
   188     foreach my $iKey0 (sort keys %$iHashRef)            # Key = service pack identifier; e.g. 'SP-1/', 'SP2/' ... Note trailing delimiter!
       
   189     {
       
   190         my $iHashRef1 = $iHashRef->{$iKey0};
       
   191         unless (ref($iHashRef1)) { next; }               # Skip occasional data item. Reference Type (if any) is 'Win32::TieRegistry'
       
   192         foreach my $iKey1 (sort keys %$iHashRef1)        # Key = hotfix reference; e.g. 'Q816093/' etc. Note trailing delimiter!
       
   193         {
       
   194             my $iHashRef2 = $iHashRef1->{$iKey1};
       
   195             unless (ref($iHashRef2)) { next; }           # Skip occasional data item. Reference Type (if any) is 'Win32::TieRegistry'
       
   196             foreach my $iKey2 (sort keys %$iHashRef2)    # Key = hotfix property; e.g. '/InstalledDate' etc. Note leading delimiter!
       
   197             {
       
   198                 if ($iKey2 =~ m/^\/InstalledDate/)
       
   199                 {
       
   200                     $iKey0 =~ s/\/$//;   # Remove trailing delimiter (slash) from service pack identifier
       
   201                     $iKey1 =~ s/\/$//;   # Remove trailing delimiter (slash) from hotfix reference
       
   202                     $iHotFixList{"$iKey0 $iKey1"}= $iHashRef2->{$iKey2};
       
   203                 }
       
   204             }
       
   205         }
       
   206     }
       
   207 
       
   208     return ($iWinEnv, %iHotFixList);
       
   209 }
       
   210 
       
   211 # GetToolInfo
       
   212 #
       
   213 # Description
       
   214 #   Collects OS information and versions of known tools.
       
   215 #
       
   216 # Inputs - None
       
   217 #
       
   218 # Returns
       
   219 #   %iToolList - Tool versions
       
   220 #
       
   221 sub GetToolInfo
       
   222 {
       
   223     my %iToolList;
       
   224     my $iToolName;
       
   225 
       
   226     GetPerlInfo(\%iToolList);
       
   227     
       
   228     GetMetrowerksInfo(\%iToolList);
       
   229 
       
   230     GetArmInfo(\%iToolList);
       
   231 
       
   232     GetJavaInfo(\%iToolList);
       
   233 
       
   234     # Location of reltools is assumed to be C:\Apps\RelTools\
       
   235     $iToolName = 'RelTools';
       
   236     my $iRelToolsVerTxt = 'C:\\Apps\\RelTools\\Version.txt';
       
   237     $iToolList{$iToolName}{'version'} = 'Unknown';
       
   238 
       
   239     if (-e $iRelToolsVerTxt)
       
   240     {
       
   241         my @iReltools = `type $iRelToolsVerTxt 2>&1`;
       
   242         # Get RelTools version (must start with numeric value). Assumed to be in first line of file
       
   243         if ($iReltools[0] =~ m/(^[0-9]{0,2}[0-9]{0,2}.*)(\n$)/) {
       
   244             $iToolList{$iToolName}{'version'} = $1;
       
   245         }
       
   246     }
       
   247     
       
   248     # Perforce Client (Typical output "Rev. P4/NTX86/2003.2/56831 (2004/04/13).")
       
   249     my $iToolNameVer = 'Perforce version';
       
   250     my $iToolNameRel = 'Perforce release';
       
   251     my @iP4Env = `P4 -V 2>&1`;
       
   252     $iToolList{$iToolNameVer}{'version'} =  'Unknown';
       
   253     $iToolList{$iToolNameRel}{'version'} =  'Unknown';
       
   254     foreach (@iP4Env)
       
   255     {
       
   256         if (m/Rev\.\s+(\S+)\s+\((.+)\)/)
       
   257         {
       
   258             $iToolList{$iToolNameVer}{'version'} =  $1;
       
   259             $iToolList{$iToolNameRel}{'version'} =  $2;
       
   260         }
       
   261     }
       
   262 
       
   263     # NSIS Compiler
       
   264     $iToolName = 'NSIS version';
       
   265     my @iNSIS_ver = `MakeNSIS.exe /VERSION 2>&1`;
       
   266     $iToolList{$iToolName}{'version'} =  'Unknown';
       
   267     if ($iNSIS_ver[0] =~ m/v(\d+\.\d+)/i)
       
   268     {
       
   269         $iToolList{$iToolName}{'version'} =  $1;
       
   270     }
       
   271 
       
   272     # PsKill utility (SysInternals)
       
   273     # PsKill v1.11 - Terminates processes on local or remote systems
       
   274     $iToolName = 'PsKill';
       
   275     my @iPSKillVer = `PsKill 2>&1`;
       
   276     $iToolList{$iToolName}{'version'} = 'Unknown';
       
   277     foreach (@iPSKillVer)
       
   278     {
       
   279         if (m/PsKill v(\d+\.\d+)/) { $iToolList{$iToolName}{'version'} =  $1; last;}
       
   280     }
       
   281 
       
   282     GetSophosInfo(\%iToolList);    # Sophos Anti-virus
       
   283 
       
   284     GetMcAfeeInfo(\%iToolList);    # McAfee Anti-virus
       
   285 
       
   286     GetGPGInfo(\%iToolList);       # GPG (Command line encryption program)
       
   287 
       
   288     GetWinTapInfo(\%iToolList);    # Win-TAP
       
   289     
       
   290     return %iToolList;  
       
   291 }
       
   292 
       
   293 # GetPerlInfo
       
   294 #
       
   295 # Description
       
   296 #   Gets Perl Version (currently usually 5.6.1 or, on a few special machines, 5.8.7)
       
   297 #   If Perl is found, we go on to list Perl Modules using "PPM query" but under Perl Version
       
   298 #
       
   299 # Inputs - Reference to Tool List hash (for return of data)
       
   300 #
       
   301 # Outputs - Data returned via supplied hashref
       
   302 #
       
   303 sub GetPerlInfo
       
   304 {
       
   305     my $iToolList = shift;
       
   306 # Typical output from "Perl -v"
       
   307 # This is perl, v5.6.1 built for MSWin32-x86-multi-thread
       
   308 # (with 1 registered patch, see perl -V for more detail)
       
   309 #
       
   310 # Copyright 1987-2001, Larry Wall
       
   311 #
       
   312 # Binary build 635 provided by ActiveState Corp. http://www.ActiveState.com
       
   313 # Built 15:34:21 Feb  4 2003
       
   314 #
       
   315     my $iToolName = 'Perl';
       
   316     my $iVersion;
       
   317     my $iBuildNum;
       
   318     my @iRetData     = `perl -v 2>&1`;
       
   319     $iToolList->{$iToolName}{'version'} = 'Unknown';
       
   320     foreach (@iRetData)
       
   321     {	# Analyse output from "Perl -v"
       
   322         if (m/ (version\s+|v)([0-9]{0,2}\.[0-9]{0,3}[_\.][0-9]{0,2})/)
       
   323         {
       
   324             if ($iVersion) {  print "ERROR: Perl Version redefined as $2.\n"; last; }	# Error? Can't have version defined twice!?
       
   325             $iVersion = $2;
       
   326             my $iMatchStr = '^([-\\w]+)\\s+\\[([.\\d]+)\\s*\\]';
       
   327             my @iRetData     = `ppm query 2>&1`;				# Ask PPM for a list of modules
       
   328             if ($iRetData[0] =~ m/No query result sets -- provide a query term\./i) # This is the response from Perl v5.8.7. Try new-style query!
       
   329             {
       
   330                 $iMatchStr = '([\\-\\w]+)\\s+\\[([\\.\\d]+\w?)([\\~\\]])';
       
   331                 `ppm set fields \"name version\" 2>&1`;			# Specified required fields. CAUTION: PPM remembers settings from previous "PPM query" call
       
   332                 @iRetData     = `ppm query * 2>&1`;				# Ask PPM for a list of modules
       
   333             }
       
   334             foreach (@iRetData)
       
   335             {	# Analyse list of modules 
       
   336                 if (m/$iMatchStr/)
       
   337                 {
       
   338                     $iToolList->{$iToolName}{'modules'}{$1} = ($3 eq '~')? $2 . $3: $2;
       
   339                 }
       
   340             }
       
   341             # Check for Inline-Java (which somehow escapes the attention of PPM
       
   342             my $iModuleName = 'Inline-Java';
       
   343             $iToolList->{$iToolName}{'modules'}{$iModuleName} = GetPerlModuleInfo($iModuleName);
       
   344             # Check for XML-DOM (earlier installations also escaped PPM)
       
   345             $iModuleName = 'XML-DOM';
       
   346             unless (defined $iToolList->{$iToolName}{'modules'}{$iModuleName})
       
   347             {
       
   348                 $iToolList->{$iToolName}{'modules'}{$iModuleName} = GetPerlModuleInfo($iModuleName);
       
   349             }
       
   350         }
       
   351         elsif (m/Binary\s+build\s+(\d+)/i)
       
   352         {
       
   353           if ($iBuildNum) { print "ERROR: Perl Build Number  redefined as $1.\n"; last; }	# Error? Can't have build defined twice!?
       
   354           $iBuildNum = $1;
       
   355         }
       
   356     }       # End foreach (@iRetData)
       
   357     # We have already set $iToolList->{$iToolName}{'version'} = 'Unknown';
       
   358     # So if $iVersion is still undefined, leave well alone! Eventually return 'Unknown'.
       
   359     if ($iVersion)        # Found version. Have we got a Build Number?
       
   360     {
       
   361         unless($iBuildNum) { $iBuildNum = 'Build unknown'; }
       
   362         $iToolList->{$iToolName}{'version'} = "$iVersion [$iBuildNum]";
       
   363     }
       
   364     # Next look for "Multiple Versions"
       
   365     # For example "\\LON-ENGBUILD54\C$\Apps\Perl.5.10.0"
       
   366     my $iAppsRoot = 'C:\Apps';
       
   367     my $iPerlExe  = 'bin\Perl.exe';
       
   368     my $iAppsDirs = ReadDirectory($iAppsRoot);      # Get arrayref
       
   369     unless (defined $iAppsDirs)
       
   370     {
       
   371         print "ERROR:  Failed to read Apps Directory.\n";
       
   372         return;
       
   373     }
       
   374     foreach my $iAppsDir (@$iAppsDirs)
       
   375     {
       
   376         if ($iAppsDir =~ m/^Perl\.(\d.*)/i)
       
   377         {
       
   378             my $iMultiVer = $1;
       
   379             $iAppsDir = uc $iAppsDir;       # Source is a Windows directory name, which could be in any case
       
   380             $iToolList->{$iToolName}{'multiver'}{$iMultiVer} =  'Unknown';
       
   381             $iVersion = '';
       
   382             $iBuildNum = '';
       
   383             my @iPerlExeRet = `$iAppsRoot\\$iAppsDir\\$iPerlExe -v`;
       
   384             foreach (@iPerlExeRet)
       
   385             {
       
   386                 if (m/ (version\s+|v)([0-9]{0,2}\.[0-9]{0,3}[_\.][0-9]{0,2})/)
       
   387                 { 
       
   388                     if ($iVersion) { print "ERROR: Perl Version  redefined as $2.\n"; last; }	# Error? Can't have version defined twice!?
       
   389                     $iVersion =  $2;
       
   390                 }
       
   391                 elsif (m/Binary\s+build\s+(\d+)/i)
       
   392                 {
       
   393                     if ($iBuildNum) { print "ERROR: Perl Build Number  redefined as $1.\n"; last; }	# Error? Can't have build defined twice!?
       
   394                     $iBuildNum = $1;
       
   395                 }
       
   396             }
       
   397             # We have already set $iToolList->{$iToolName}{'multiver'}{$iMultiVer} = 'Unknown';
       
   398             # So if $iVersion is still undefined, leave well alone! Eventually return 'Unknown'.
       
   399             if ($iVersion)        # Found version. Have we got a Build Number?
       
   400             {
       
   401                 unless($iBuildNum) { $iBuildNum = 'Build unknown'; }
       
   402                 $iToolList->{$iToolName}{'multiver'}{$iMultiVer} = "$iVersion [$iBuildNum]";
       
   403             }
       
   404         }
       
   405     }       # End foreach my $iAppsDir (@$iAppsDirs)
       
   406 }
       
   407 
       
   408 # GetPerlModuleInfo
       
   409 #
       
   410 # Description
       
   411 #   Gets Version for the specified Perl Module 
       
   412 #
       
   413 # Inputs - Name of Module 
       
   414 #
       
   415 # Retuens - Version text
       
   416 #
       
   417 sub GetPerlModuleInfo
       
   418 {
       
   419     my $iModuleName = shift;
       
   420     my $iVerTxt = 'Unknown';
       
   421     $iModuleName =~ s/-/::/;
       
   422     if (eval "require $iModuleName;")
       
   423     {
       
   424         no strict 'refs';
       
   425         $iVerTxt = ${$iModuleName . "::VERSION"};
       
   426         use strict;
       
   427     }
       
   428     return $iVerTxt;
       
   429 }
       
   430 
       
   431 # GetMetrowerksInfo
       
   432 #
       
   433 # Description
       
   434 #   Gets Metrowerks Compiler and Linker Versions
       
   435 #
       
   436 # Inputs - Reference to Tool List hash (for return of data)
       
   437 #
       
   438 # Outputs - Data returned via supplied hashref
       
   439 #
       
   440 sub GetMetrowerksInfo
       
   441 {
       
   442 
       
   443 
       
   444     my $iToolList = shift;
       
   445 
       
   446     # First get the version of the default Compiler (MWCCSym2), as located by the "permanent" PATH etc.
       
   447     my $iToolNameCC = 'Metrowerks Compiler';
       
   448     $iToolList->{$iToolNameCC}{'version'} = 'Unknown';
       
   449     my @iCCRet     = `mwccsym2 -version 2>&1`;
       
   450     foreach (@iCCRet)
       
   451     {
       
   452         if (m/Version(.*)(\n$)/)
       
   453         {
       
   454             $iToolList->{$iToolNameCC}{'version'} =  $1;
       
   455             last;
       
   456         }
       
   457     }
       
   458     
       
   459     # Now get the version of the default Linker (MWLDSym2), as located by the "permanent" PATH etc.
       
   460     my $iToolNameLD = 'Metrowerks Linker';
       
   461     my @iLDEnv     = `mwldsym2 -version 2>&1`;
       
   462     $iToolList->{$iToolNameLD}{'version'} = 'Unknown';
       
   463     foreach (@iLDEnv) 
       
   464     {
       
   465         if (m/Version(.*)(\n$)/) 
       
   466         { 
       
   467             $iToolList->{$iToolNameLD}{'version'} =  $1;
       
   468             last;
       
   469         }
       
   470     }
       
   471 
       
   472     # Next look for "Multiple Versions"
       
   473     # For example "\\LON-ENGBUILD54\C$\Apps\Metrowerks\OEM3.1.1\Symbian_Tools\Command_Line_Tools\mwccsym2.exe"
       
   474     my $iMWksRoot = 'C:\Apps\Metrowerks';
       
   475     my $iMWksCC   = 'Symbian_Tools\Command_Line_Tools\mwccsym2.exe';
       
   476     my $iMWksLD   = 'Symbian_Tools\Command_Line_Tools\mwldsym2.exe';
       
   477     my $iMWksDirs = ReadDirectory($iMWksRoot);      # Get arrayref
       
   478     unless (defined $iMWksDirs)
       
   479     {
       
   480         print "ERROR:  Failed to read Metrowerks Root Directory.\n";
       
   481         return;
       
   482     }
       
   483     foreach my $iMWksDir (@$iMWksDirs)
       
   484     {
       
   485         if ($iMWksDir =~ m/^OEM\d+\.\d+/i)
       
   486         {
       
   487             $iMWksDir = uc $iMWksDir;       # Source is a Windows directory name, which could be in any case
       
   488             my @iMWksCCRet = `$iMWksRoot\\$iMWksDir\\$iMWksCC`;
       
   489             $iToolList->{$iToolNameCC}{'multiver'}{$iMWksDir} =  'Unknown';
       
   490             foreach my $iLine(@iMWksCCRet)
       
   491             {
       
   492                 if ($iLine =~ m/Version(.*)(\n$)/i)
       
   493                 { 
       
   494                     $iToolList->{$iToolNameCC}{'multiver'}{$iMWksDir} =  $1;
       
   495                     last;
       
   496                 }
       
   497             }
       
   498             my @iMWksLDRet = `$iMWksRoot\\$iMWksDir\\$iMWksLD`;
       
   499             $iToolList->{$iToolNameLD}{'multiver'}{$iMWksDir} =  'Unknown';
       
   500             foreach my $iLine(@iMWksLDRet)
       
   501             {
       
   502                 if ($iLine =~ m/Version(.*)(\n$)/i)
       
   503                 { 
       
   504                     $iToolList->{$iToolNameLD}{'multiver'}{$iMWksDir} =  $1;
       
   505                     last;
       
   506                 }
       
   507             }
       
   508         }
       
   509     }       # End foreach my $iMWksDir (@$iMWksDirs)
       
   510 
       
   511 }
       
   512 
       
   513 # GetArmInfo
       
   514 #
       
   515 # Description
       
   516 #   Looks for directories below C:\Apps\ARM which might contain versions of RVCT compiler etc.
       
   517 #
       
   518 # Inputs - Reference to Tool List hash (for return of data)
       
   519 #
       
   520 # Outputs - Data returned via supplied hashref
       
   521 #
       
   522 sub GetArmInfo
       
   523 {
       
   524     my $iToolList = shift;
       
   525     my $iToolName = 'Arm CC';
       
   526 
       
   527     # First get the version of the default ARMCC, as located by the "permanent" PATH etc.
       
   528     $iToolList->{$iToolName}{'version'} = 'Unknown';
       
   529     my @iArmCCRet = `armcc --vsn 2>&1`;
       
   530     foreach (@iArmCCRet) 
       
   531     {
       
   532         if (m/RVCT(.*)(\n$)/)
       
   533         { 
       
   534             $iToolList->{$iToolName}{'version'} =  $1;
       
   535             last;
       
   536         }
       
   537     }
       
   538     # Next look for "Multiple Versions"
       
   539     # For example "\\LON-ENGBUILD51\C$\Apps\ARM\RVCT2.2[435]\RVCT\Programs\2.2\349\win_32-pentium\armcc.exe" 
       
   540     my $iRVCTRoot = 'C:\Apps\ARM';
       
   541     my $iRVCTCC2  = 'RVCT\Programs\2.2\349\win_32-pentium\armcc.exe';   # Applies to RVCT Version 2.x
       
   542     my $iRVCTCC3  = 'bin\armcc.exe';                                    # Applies to RVCT Version 3.x
       
   543     my $iRVCTDirs = ReadDirectory($iRVCTRoot);      # Get arrayref
       
   544     unless (defined $iRVCTDirs)
       
   545     {
       
   546         print "ERROR:  Failed to read ARM Root Directory.\n";
       
   547         return;
       
   548     }
       
   549     foreach my $iRVCTDir (@$iRVCTDirs)     # Applies to RVCT Version 2.x
       
   550     {
       
   551         $iRVCTDir = uc $iRVCTDir;          # Source is a Windows directory name, which could be in any case
       
   552         if ($iRVCTDir =~ m/^RVCT2\.\d+/i)
       
   553         {
       
   554             $iToolList->{$iToolName}{'multiver'}{$iRVCTDir} = GetArmVersion("$iRVCTRoot\\$iRVCTDir\\$iRVCTCC2");
       
   555         }
       
   556         elsif ($iRVCTDir =~ m/^RVCT\d+\.\d+/i)   # Applies to RVCT Version 3.x (and above, until we know otherwise!!)
       
   557         {
       
   558             $iToolList->{$iToolName}{'multiver'}{$iRVCTDir} = GetArmVersion("$iRVCTRoot\\$iRVCTDir\\$iRVCTCC3");
       
   559         }
       
   560     }
       
   561 
       
   562 }
       
   563 
       
   564 # GetArmVersion
       
   565 #
       
   566 # Description
       
   567 #   Gets Arm Compiler Version for a specified instance.
       
   568 #
       
   569 # Inputs - Full pathname of compiler (ARMCC.EXE)
       
   570 #
       
   571 # Outputs - Version (as text) or 'Unknown' if not determined
       
   572 #
       
   573 sub GetArmVersion
       
   574 {
       
   575     my $iRVCTCC = shift;       # Full pathname of compiler (ARMCC.EXE)
       
   576     my @iArmCCEnv = `$iRVCTCC --vsn 2>&1`;
       
   577     foreach my $iLine(@iArmCCEnv)
       
   578     {
       
   579         if ($iLine =~ m/RVCT(.*)(\n$)/i)
       
   580         { 
       
   581             return $1;
       
   582         }
       
   583     }
       
   584     return 'Unknown';
       
   585 }
       
   586 
       
   587 # GetJavaInfo
       
   588 #
       
   589 # Description
       
   590 #   Gets Java Runtime Compiler Version
       
   591 #
       
   592 # Inputs - Reference to Tool List hash (for return of data)
       
   593 #
       
   594 # Outputs - Data returned via supplied hashref
       
   595 #
       
   596 sub GetJavaInfo
       
   597 {
       
   598     my $iToolList = shift;
       
   599     my $iToolName = 'Java';
       
   600 
       
   601     # First get the version of the default Java installation as located by the "permanent" PATH etc.
       
   602     # This probably means running 
       
   603     my @iJavaReturn     = `java -version 2>&1`;
       
   604     $iToolList->{$iToolName}{'version'} =  'Unknown';
       
   605     foreach my $iLine (@iJavaReturn) 
       
   606     {
       
   607         if ($iLine =~ m/version.*(\"{1})(.*)(\"{1})/i)
       
   608         {
       
   609             $iToolList->{$iToolName}{'version'} =  $2;
       
   610             last;
       
   611         }
       
   612     }
       
   613 
       
   614     # Next look for "Multiple Versions" - Assumed to be in directories matching 'C:\Apps\JRE*'
       
   615     # For example "C:\Apps\JRE1.5.0_13\bin\java.exe" 
       
   616     my $iJRERoot = 'C:\Apps';
       
   617     my $iJREEXE  = 'bin\java.exe';
       
   618     my $iJREDirs = ReadDirectory($iJRERoot);      # Get arrayref (list of sub-directories
       
   619     unless (defined $iJREDirs)
       
   620     {
       
   621         print "ERROR:  Failed to read JRE Root Directory: $iJRERoot.\n";
       
   622         return;
       
   623     }
       
   624     foreach my $iJREDir (@$iJREDirs)
       
   625     {
       
   626         if ($iJREDir =~ m/^JRE\d+\.\d+/i)
       
   627         {
       
   628             $iJREDir = uc $iJREDir;       # Source is a Windows directory name, which could be in any case
       
   629             my @iJREReturn = `$iJRERoot\\$iJREDir\\$iJREEXE -version 2>&1`;
       
   630             $iToolList->{$iToolName}{'multiver'}{$iJREDir} =  'Unknown';
       
   631             foreach my $iLine(@iJREReturn)
       
   632             {
       
   633                 if ($iLine =~ m/version.*(\"{1})(.*)(\"{1})/i)
       
   634                 { 
       
   635                     $iToolList->{$iToolName}{'multiver'}{$iJREDir} =  $2;
       
   636                     last;
       
   637                 }
       
   638             }
       
   639         }
       
   640     }
       
   641 
       
   642 }
       
   643 
       
   644 # GetSophosInfo
       
   645 #
       
   646 # Description
       
   647 #   Gets Sophos Version
       
   648 #
       
   649 # Inputs - Reference to Tool List hash (for return of data)
       
   650 #
       
   651 # Outputs - Data returned via supplied hashref
       
   652 #
       
   653 sub GetSophosInfo
       
   654 {
       
   655     my $iToolList = shift;
       
   656     # Sophos Anti-virus
       
   657     # Typical output from "sav32cli.exe -v"
       
   658     # Sophos Anti-Virus
       
   659     # Copyright (c) 1989-2005 Sophos Plc, www.sophos.com
       
   660     # System time 11:58:18, System date 04 April 2005
       
   661     # Product version           : 3.92.0
       
   662     # Engine version            : 2.28.10
       
   663     # Virus data version        : 3.92
       
   664     # User interface version    : 2.03.048
       
   665     # Platform                  : Win32/Intel
       
   666     # Released                  : 04 April 2005
       
   667     # Total viruses (with IDEs) : 102532
       
   668     my $iSophosExe='C:\Program Files\Sophos SWEEP for NT\sav32cli.exe';
       
   669     $iToolList->{'Sophos product'}{'version'} = 'Unknown';
       
   670     $iToolList->{'Sophos data'}{'version'} = 'Unknown';
       
   671     $iToolList->{'Sophos release'}{'version'} = 'Unknown';
       
   672     if (-e $iSophosExe)
       
   673     {
       
   674         my @iSophosVer = `\"$iSophosExe\" -v`;
       
   675 
       
   676         # Get Sophos versions
       
   677         foreach my $iLine (@iSophosVer)
       
   678         {
       
   679             if ($iLine =~ m/Product\s+version\s+:\s+(\S+)/)
       
   680             {
       
   681                 $iToolList->{'Sophos product'}{'version'} = $1;
       
   682                 next;
       
   683             }
       
   684             if ($iLine =~ m/Virus\s+data\s+version\s+:\s+(\S+)/)
       
   685             {
       
   686                 $iToolList->{'Sophos data'}{'version'} = $1;
       
   687                 next;
       
   688             }
       
   689             if ($iLine =~ m/Released\s+:\s+(.+)/)
       
   690             {
       
   691                 $iToolList->{'Sophos release'}{'version'} = $1;
       
   692                 next;
       
   693             }
       
   694         }
       
   695     }
       
   696 
       
   697 }
       
   698 
       
   699 # GetMcAfeeInfo
       
   700 #
       
   701 # Description
       
   702 #   Gets McAfee Versions (Software and data)
       
   703 #
       
   704 # Inputs - Reference to Tool List hash (for return of data)
       
   705 #
       
   706 # Outputs - Data returned via supplied hashref
       
   707 #
       
   708 sub GetMcAfeeInfo
       
   709 {
       
   710     my $iToolList = shift;
       
   711     # McAfee Anti-virus
       
   712     # Revision March 2007 - Get Versions from Registry in the following location (for Version 8.000?):
       
   713     # HKEY_LOCAL_MACHINE\SOFTWARE\Network Associates\ePolicy Orchestrator\Application Plugins\VIRUSCAN8000
       
   714     $iToolList->{'McAfee VirusScan'}{'version'} = 'Unknown';
       
   715     $iToolList->{'McAfee VirusData'}{'version'} = 'Unknown';
       
   716 
       
   717     my $iRegKey = 'LMachine/SOFTWARE/Network Associates/ePolicy Orchestrator/Application Plugins';
       
   718     # Get data from hash set up by Win32::TieRegistry
       
   719     my $iHashRef = $Registry->{$iRegKey};
       
   720     unless (defined $iHashRef) { print "WARNING: Failed to read McAfee version from Registry\n";  return; }
       
   721     my @iValidHashKeys;
       
   722     foreach my $iHashKey (sort %$iHashRef)
       
   723     {
       
   724         if ($iHashKey =~ m/^VIRUSCAN\d+/i)
       
   725         {
       
   726             push @iValidHashKeys,$iHashKey;
       
   727         }
       
   728     }
       
   729     unless (scalar @iValidHashKeys)
       
   730     {
       
   731         return;             # No valid sub-key
       
   732     }
       
   733     if ((scalar @iValidHashKeys) > 1)
       
   734     {
       
   735         print "WARNING: Duplicate McAfee Versions.\n"; 
       
   736     }
       
   737     
       
   738     @iValidHashKeys = sort @iValidHashKeys;
       
   739     my $iVersionKey = pop @iValidHashKeys;      # In the unlikely event of there being more than one, get the last one!
       
   740     
       
   741     # Check that hash element exists before referencing data. Otherwise TieRegistry will think that we want to create a new key/value
       
   742     if (defined $iHashRef->{$iVersionKey}{'/Version'})    { $iToolList->{'McAfee VirusScan'}{'version'} = $iHashRef->{$iVersionKey}{'/Version'}; }
       
   743     if (defined $iHashRef->{$iVersionKey}{'/DATVersion'}) { $iToolList->{'McAfee VirusData'}{'version'} = $iHashRef->{$iVersionKey}{'/DATVersion'}; }
       
   744 }
       
   745 
       
   746 # GetGPGInfo
       
   747 #
       
   748 # Description
       
   749 #   Gets GPG Version (currently usually 
       
   750 #
       
   751 # Inputs - Reference to Tool List hash (for return of data)
       
   752 #
       
   753 # Outputs - Data returned via supplied hashref
       
   754 #
       
   755 sub GetGPGInfo
       
   756 {
       
   757     my $iToolList = shift;
       
   758 
       
   759     # Typical output from 'GPG -h'
       
   760     #     gpg (GnuPG) 1.4.4
       
   761     # Copyright (C) 2006 Free Software Foundation, Inc.
       
   762     # This program comes with ABSOLUTELY NO WARRANTY.
       
   763     # This is free software, and you are welcome to redistribute it
       
   764     # under certain conditions. See the file COPYING for details.
       
   765 
       
   766     my $iToolName = 'GnuPG';
       
   767     my @iRetData     = `GPG -h 2>&1`;
       
   768     $iToolList->{$iToolName}{'version'} = 'Unknown';
       
   769     foreach (@iRetData)
       
   770     {
       
   771         if (m/^\s*gpg\s+\(GnuPG\)\s*(\d+\.\d+\.\d+)/i)
       
   772         {
       
   773             $iToolList->{$iToolName}{'version'} = $1;
       
   774             last;
       
   775         }
       
   776     }
       
   777 }
       
   778 
       
   779 # GetWinTapInfo
       
   780 #
       
   781 # Description
       
   782 #   Gets WinTap Version
       
   783 #
       
   784 # Inputs - Reference to Tool List hash (for return of data)
       
   785 #
       
   786 # Outputs - Data returned via supplied hashref
       
   787 #
       
   788 sub GetWinTapInfo
       
   789 {
       
   790     my $iToolList = shift;
       
   791 
       
   792     # Typical output from 'IPCONFIG /ALL'
       
   793     # Ethernet adapter TAP-Win32:
       
   794     #    Connection-specific DNS Suffix  . :
       
   795     #    Description . . . . . . . . . . . : TAP-Win32 Adapter V8
       
   796     my $iToolName = 'WinTAP';
       
   797     my @iRetData     = `IPCONFIG /ALL 2>&1`;
       
   798     $iToolList->{$iToolName}{'version'} = 'Unknown';
       
   799     foreach (@iRetData)
       
   800     {
       
   801         if (m/Description.+TAP-Win32\s+Adapter\s+(V.+)/i)
       
   802         {
       
   803             $iToolList->{$iToolName}{'version'} = $1;
       
   804             last;
       
   805         }
       
   806     }
       
   807 }
       
   808 
       
   809 # ReadDirectory
       
   810 #
       
   811 # Read specified directory. Remove '.' and '..' entries (Windows speciality!)
       
   812 #
       
   813 # Input: Directory name
       
   814 #
       
   815 # Return: Array of subdirectory names, or undef if open fails.
       
   816 #
       
   817 sub ReadDirectory
       
   818 {
       
   819     my $iDirName = shift;
       
   820 
       
   821 	unless (opendir DIRECTORY, $iDirName)
       
   822 	{
       
   823 	    print ("ERROR:  Failed to open directory: $iDirName\nERROR:  $!\n");
       
   824 	    return undef;
       
   825 	}
       
   826 	my @iSubDirs = readdir(DIRECTORY);
       
   827 	closedir DIRECTORY;
       
   828 
       
   829 	# Remove '.' and '..' from list
       
   830 	for (my $iIndx = 0; $iIndx < (scalar @iSubDirs); )
       
   831 	{
       
   832 	    if ($iSubDirs[$iIndx] =~ m/^\.{1,2}/)
       
   833 	    {
       
   834 	        splice @iSubDirs, $iIndx, 1;
       
   835 	    }
       
   836 	    else
       
   837 	    {
       
   838 	        ++$iIndx;
       
   839 	    }
       
   840 	}
       
   841 
       
   842     return \@iSubDirs;
       
   843 }
       
   844 
       
   845 1;