releasing/cbrtools/perl/RelData.pm
changeset 607 378360dbbdba
parent 602 3145852acc89
equal deleted inserted replaced
591:22486c9c7b15 607:378360dbbdba
       
     1 # Copyright (c) 2000-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 the License "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 #
       
    16 
       
    17 package RelData;
       
    18 
       
    19 use strict;
       
    20 use Data::Dumper;
       
    21 use MrpData;
       
    22 use PathData;
       
    23 
       
    24 #
       
    25 # Data version history.
       
    26 #
       
    27 # 1 - Original release.
       
    28 # 2 - Added 'relToolsVer' tag.
       
    29 #
       
    30 
       
    31 
       
    32 #
       
    33 # Public.
       
    34 #
       
    35 
       
    36 sub New {
       
    37   my $pkg = shift;
       
    38   my $self = {};
       
    39   bless $self, $pkg;
       
    40   $self->{iniData} = shift;
       
    41   $self->{mrpData} = shift;
       
    42   $self->{notesSrc} = shift;
       
    43   $self->{data}->{env} = shift;
       
    44   $self->{data}->{toolName} = shift;
       
    45   $self->{verbose} = shift;
       
    46   $self->{dontPersist} = shift;
       
    47   $self->{project} = shift;
       
    48 
       
    49   $self->{comp} = $self->{mrpData}->Component();
       
    50   $self->{ver} = $self->{mrpData}->ExternalVersion();
       
    51   $self->{data}->{dataFormatVer} = 2;
       
    52   $self->{data}->{intVer} = $self->{mrpData}->InternalVersion();
       
    53   $self->{data}->{mrpName} = $self->{mrpData}->MrpName();
       
    54   $self->{data}->{relToolsVer} = Utils::ToolsVersion();
       
    55   $self->{data}->{notesSrc}->{srcFilterErrors} = $self->{mrpData}->SourceFilterErrors();
       
    56   $self->{data}->{notesSrc}->{date} = localtime;
       
    57   
       
    58   foreach my $srcitem (keys %{$self->{mrpData}->SourceItems()}) {
       
    59     if($self->{iniData}->HasMappings()){
       
    60       $srcitem = $self->{iniData}->PerformReverseMapOnFileName($srcitem);
       
    61       $srcitem = Utils::RemoveSourceRoot($srcitem);
       
    62     }
       
    63 
       
    64     $self->{data}->{srcitems}->{$srcitem} = 1;
       
    65   }
       
    66 
       
    67   unless(defined $self->{data}->{srcitems}){
       
    68     $self->{data}->{srcitems} = $self->{mrpData}->SourceItems();
       
    69   }
       
    70   
       
    71   $self->{data}->{envUserName} = ($ENV{FirstName} || '') . " " . ($ENV{LastName} || '');
       
    72   $self->ParseNotesSource();
       
    73   $self->WorkOutFirstCompatibleVersion();
       
    74   unless (defined $self->{dontPersist}) {
       
    75     $self->WriteToFile();
       
    76   }
       
    77   return $self;
       
    78 }
       
    79 
       
    80 sub Open {
       
    81   my $pkg = shift;
       
    82   my $self = {};
       
    83   bless $self, $pkg;
       
    84   $self->{iniData} = shift;
       
    85   $self->{comp} = shift;
       
    86   $self->{ver} = shift;
       
    87   $self->{verbose} = shift;
       
    88   $self->ReadFromFile();
       
    89   return $self;
       
    90 }
       
    91 
       
    92 sub OpenExternal {
       
    93   my $pkg = shift;
       
    94   my $externalArchive = shift;
       
    95   my $comp = shift;
       
    96   my $ver = shift;
       
    97   my $self = {};
       
    98   $self->{comp} = $comp;
       
    99   $self->{ver} = $ver;
       
   100   my $externalFile = File::Spec->catdir($externalArchive, $comp, $ver);
       
   101   bless $self, $pkg;
       
   102   $self->ReadFromSpecificFile($externalFile);
       
   103   return $self;
       
   104 }
       
   105 
       
   106 
       
   107 sub OpenSet {
       
   108   my $pkg = shift;
       
   109   my $iniData = shift;
       
   110   my $comp = shift;
       
   111   my $verbose = shift;
       
   112   my $versionfilter = shift;
       
   113   
       
   114   my @relDataObjects;
       
   115   foreach my $ver (@{$iniData->PathData->ListVersions($comp, 0, $versionfilter, $iniData->LatestVerFilter)}) {
       
   116     my $thisRelData = {};
       
   117     bless $thisRelData, $pkg;
       
   118     eval {
       
   119       # ReadFromFile may die, if the file is corrupt.
       
   120       # In which case we do not add it to the set.
       
   121       $thisRelData->{iniData} = $iniData;
       
   122       $thisRelData->{comp} = $comp;
       
   123       $thisRelData->{ver} = $ver;
       
   124       $thisRelData->{verbose} = $verbose;
       
   125       $thisRelData->ReadFromFile();
       
   126       push (@relDataObjects, $thisRelData);
       
   127     };
       
   128     print "Warning: could not examine \"$comp\" \"$ver\" because $@" if ($@);
       
   129   }
       
   130   
       
   131   @relDataObjects = sort { $b->ReleaseTime() <=> $a->ReleaseTime() } @relDataObjects;
       
   132 
       
   133   return \@relDataObjects;;
       
   134 }
       
   135 
       
   136 sub Component {
       
   137   my $self = shift;
       
   138   die unless exists $self->{comp};
       
   139   return $self->{comp};
       
   140 }
       
   141 
       
   142 sub MadeWith {
       
   143   my $self = shift;
       
   144   my $ver = $self->{data}->{relToolsVer} || "(unknown version)";
       
   145   my $tool = $self->{data}->{toolName} || "(unknown tool)";
       
   146   return "$tool $ver";
       
   147 }
       
   148 
       
   149 sub MadeWithVersion {
       
   150   my $self = shift;
       
   151   return "".$self->{data}->{relToolsVer};
       
   152 }
       
   153 
       
   154 sub SourceIncluded {
       
   155   my $self = shift;
       
   156   my $items;
       
   157   eval {
       
   158     $items = $self->SourceItems();
       
   159   };
       
   160   return "(unknown)" if $@;
       
   161   return join (", ", keys %$items);
       
   162 }
       
   163 
       
   164 sub Version {
       
   165   my $self = shift;
       
   166   die unless exists $self->{ver};
       
   167   return $self->{ver};
       
   168 }
       
   169 
       
   170 sub InternalVersion {
       
   171   my $self = shift;
       
   172   die unless exists $self->{data};
       
   173   return $self->{data}->{intVer};
       
   174 }
       
   175 
       
   176 sub MrpName {
       
   177   my $self = shift;
       
   178   die unless exists $self->{data};
       
   179   return $self->{data}->{mrpName};
       
   180 }
       
   181 
       
   182 sub FirstCompatibleVersion {
       
   183   my $self = shift;
       
   184   die unless exists $self->{data};
       
   185   return $self->{data}->{firstCompatibleVersion};
       
   186 }
       
   187 
       
   188 sub Environment {
       
   189   my $self = shift;
       
   190   die unless exists $self->{data};
       
   191   return $self->{data}->{env};
       
   192 }
       
   193 
       
   194 sub NotesSource {
       
   195   my $self = shift;
       
   196   die unless exists $self->{data};
       
   197   return $self->{data}->{notesSrc};
       
   198 }
       
   199 
       
   200 sub UpdateProject {
       
   201   my $self = shift;
       
   202   $self->{project} = shift;
       
   203   $self->WriteToFile();
       
   204 }
       
   205 
       
   206 sub UpdateNotes {
       
   207   my $self = shift;
       
   208   $self->{notesSrc} = shift;
       
   209   $self->DeleteNotesSource();
       
   210   $self->ParseNotesSource();
       
   211   $self->WriteToFile();
       
   212 }
       
   213 
       
   214 sub UpdateInternalVersion {
       
   215   my $self = shift;
       
   216   $self->{data}->{intVer} = shift;
       
   217   $self->WriteToFile();
       
   218 }
       
   219 
       
   220 sub UpdateEnv {
       
   221   my $self = shift;
       
   222   $self->{data}->{env} = shift;
       
   223   $self->WriteToFile();
       
   224 }
       
   225 
       
   226 sub ReleaseTime {
       
   227   my $self = shift;
       
   228   unless (exists $self->{releaseTime}) {
       
   229     $self->{releaseTime} = Utils::TextTimeToEpochSeconds($self->{data}->{notesSrc}->{date});
       
   230   }
       
   231   return $self->{releaseTime};
       
   232 }
       
   233 
       
   234 sub SourceItems {
       
   235   my $self = shift;
       
   236   unless (defined $self->{data}->{srcitems}) {
       
   237     my $createdver = $self->{data}->{relToolsVer} || 0;
       
   238     if (Utils::CompareVers($createdver,2.54)<0) {
       
   239       die "this release was created with Release Tools $createdver, and the necessary information is only present in releases created with 2.54 or later.\n";
       
   240     }
       
   241     die "Could not return the list of \"source\" statements used in the MRP file." 
       
   242   }
       
   243   return $self->{data}->{srcitems};
       
   244 }
       
   245 
       
   246 sub EnvUserName {
       
   247   my $self = shift;
       
   248   return $self->{data}->{envUserName};
       
   249   }
       
   250 
       
   251 #
       
   252 # Private.
       
   253 #
       
   254 
       
   255 sub WriteToFile {
       
   256   my $self = shift;
       
   257   my $relDir = $self->{iniData}->PathData->LocalArchivePathForExistingOrNewComponent($self->{comp}, $self->{ver}, $self->{project});
       
   258 
       
   259   my $file = "$relDir\\reldata";  
       
   260   
       
   261   if (-e $file) {
       
   262     Utils::SetFileWritable($file);
       
   263   }
       
   264   open (OUT, ">$file") or die "Error: Couldn't open \"$file\" for writing: $!\n";
       
   265   print OUT Data::Dumper->Dump([$self->{data}], ['self->{data}']);
       
   266   close (OUT);
       
   267   Utils::SetFileReadOnly($file);
       
   268 }
       
   269 
       
   270 sub ReadFromFile {
       
   271   my $self = shift;
       
   272   my $pathData = shift || $self->{iniData}->PathData;
       
   273 
       
   274   my $comp = $self->{comp};
       
   275   my $ver = $self->{ver};
       
   276 
       
   277   my $relDir = $pathData->LocalArchivePathForExistingComponent($comp, $ver);
       
   278   die "Error: \"$comp $ver\" does not exist\n" unless $relDir;
       
   279   die "Error: \"$comp $ver\" was not a valid release (can't find \"$relDir\\reldata\")\n" unless -e "$relDir\\reldata";
       
   280   $self->{project} = $pathData->ComponentProject($comp, $ver);
       
   281   $self->ReadFromSpecificFile($relDir);
       
   282 }
       
   283 
       
   284 sub ReadFromSpecificFile {
       
   285   my $self = shift;
       
   286   my $relDir = shift;
       
   287   unless (-e $relDir) {
       
   288     die "Error: $self->{comp} $self->{ver} does not exist\n";
       
   289   }
       
   290   my $file = "$relDir\\reldata";
       
   291   open (IN, $file) or die "Error: Couldn't open \"$file\" for reading: $!\n";
       
   292   local $/ = undef;
       
   293   my $data = <IN>;
       
   294   die "Error: Reldata in \"$relDir\" is blank" unless $data =~ (m/\S/);
       
   295   eval ($data) or die "Error: Couldn't parse reldata in \"$relDir\"\n";
       
   296   close (IN);
       
   297 }
       
   298 
       
   299 sub ParseNotesSource {
       
   300   my $self = shift;
       
   301 
       
   302   if ($self->{verbose} > 1) { print "Parsing notes source...\n"; }
       
   303 
       
   304   open(SRC,"$self->{notesSrc}") or die "Unable to open $self->{notesSrc} for reading: $!\n";
       
   305 
       
   306   my $thisTag;
       
   307   while (<SRC>) {
       
   308     if (m/^NOTESRC/i) {
       
   309       chomp;
       
   310       $thisTag = $_;
       
   311     }
       
   312     elsif (m/^\s*$/) {
       
   313       next;
       
   314     }
       
   315     elsif (defined $thisTag) {
       
   316       $self->AddLine($thisTag, $_);
       
   317     }
       
   318   }
       
   319   close SRC;
       
   320 
       
   321   $self->ValidateSource();
       
   322 }
       
   323 
       
   324 sub AddLine {
       
   325   my $self = shift;
       
   326   my $thisTag = shift;
       
   327   my $thisLine = shift;
       
   328   chomp $thisLine;
       
   329 
       
   330   if ($thisTag =~ m/^NOTESRC_RELEASER$/i) {
       
   331     $self->{data}->{notesSrc}->{releaser} = $thisLine;		
       
   332   }
       
   333   elsif ($thisTag =~ m/^NOTESRC_RELEASE_REASON$/i) {
       
   334     push @{$self->{data}->{notesSrc}->{releaseReason}}, $thisLine;
       
   335   }
       
   336   elsif ($thisTag =~ m/^NOTESRC_GENERAL_COMMENTS$/i) {
       
   337     push @{$self->{data}->{notesSrc}->{generalComments}}, $thisLine;
       
   338   }
       
   339   elsif ($thisTag =~ m/^NOTESRC_KNOWN_DEVIATIONS$/i) {
       
   340     push @{$self->{data}->{notesSrc}->{knownDeviations}}, $thisLine;
       
   341   }
       
   342   elsif ($thisTag =~ m/^NOTESRC_BUGS_FIXED$/i) {
       
   343     push @{$self->{data}->{notesSrc}->{bugsFixed}}, $thisLine;
       
   344   }
       
   345   elsif ($thisTag =~ m/^NOTESRC_BUGS_REMAINING$/i) {
       
   346     push @{$self->{data}->{notesSrc}->{bugsRemaining}}, $thisLine;
       
   347   }
       
   348   elsif ($thisTag =~ m/^NOTESRC_OTHER_CHANGES$/i) {
       
   349     push @{$self->{data}->{notesSrc}->{otherChanges}}, $thisLine;
       
   350   }
       
   351   else {
       
   352     die "Error: Unknown tag \"$thisTag\" in $self->{notesSrc}\n";
       
   353   }
       
   354 }
       
   355 
       
   356 sub ValidateSource {
       
   357   my $self = shift;
       
   358 
       
   359   if ($self->{verbose} > 1) { print "Validating notes source...\n"; }
       
   360 
       
   361   unless (exists $self->{data}->{notesSrc}->{releaser}) {
       
   362     die "Error <NOTESRC_RELEASER> not specified in $self->{notesSrc}\n";
       
   363   } 
       
   364   unless (exists $self->{data}->{notesSrc}->{releaseReason}) {
       
   365     die "Error <NOTESRC_RELEASE_REASON> not specified in $self->{notesSrc}\n";
       
   366   } 
       
   367   unless (exists $self->{data}->{notesSrc}->{generalComments}) {
       
   368     push @{$self->{data}->{notesSrc}->{generalComments}}, "<unspecified>";
       
   369   } 
       
   370   unless (exists $self->{data}->{notesSrc}->{knownDeviations}) {
       
   371     push @{$self->{data}->{notesSrc}->{knownDeviations}}, "<unspecified>";
       
   372   }
       
   373   unless (exists $self->{data}->{notesSrc}->{bugsFixed}) {
       
   374     push @{$self->{data}->{notesSrc}->{bugsFixed}}, "<unspecified>";
       
   375   }
       
   376   unless (exists $self->{data}->{notesSrc}->{bugsRemaining}) {
       
   377     push @{$self->{data}->{notesSrc}->{bugsRemaining}}, "<unspecified>";
       
   378   }
       
   379   unless (exists $self->{data}->{notesSrc}->{otherChanges}) {
       
   380     push @{$self->{data}->{notesSrc}->{otherChanges}}, "<unspecified>";
       
   381   }
       
   382 }
       
   383 
       
   384 sub DeleteNotesSource {
       
   385   my $self = shift;
       
   386   delete $self->{data}->{notesSrc}->{releaser};		
       
   387   delete $self->{data}->{notesSrc}->{releaseReason};
       
   388   delete $self->{data}->{notesSrc}->{generalComments};
       
   389   delete $self->{data}->{notesSrc}->{knownDeviations};
       
   390   delete $self->{data}->{notesSrc}->{bugsFixed};
       
   391   delete $self->{data}->{notesSrc}->{bugsRemaining};
       
   392   delete $self->{data}->{notesSrc}->{otherChanges};
       
   393 }
       
   394 
       
   395 sub WorkOutFirstCompatibleVersion {
       
   396   my $self = shift;
       
   397 
       
   398   my $version = "2.00";
       
   399   $version = "2.50" if ($self->{iniData}->CategoriseBinaries());
       
   400   $version = "2.59" if ($self->{iniData}->CategoriseExports());
       
   401   $version = "2.80.1000" if grep /[^A-GX]/, @{$self->{mrpData}->SourceCategories()}; 
       
   402   # Add to this when extra features are added which break
       
   403   # backward compatibility of release formats.
       
   404   $self->{data}->{firstCompatibleVersion} = $version;
       
   405 }
       
   406 
       
   407 sub WarnIfReleaseTooNew {
       
   408   my $self = shift;
       
   409   # Called from EnvDb::InstallComponent
       
   410   my $relversion = $self->FirstCompatibleVersion();
       
   411   return unless defined $relversion;
       
   412   my $toolsver = Utils::ToolsVersion;
       
   413   if (Utils::CompareVers($relversion,$toolsver)>0) {
       
   414     my $thisComp = $self->{comp};
       
   415     print "Warning: $thisComp requires Release Tools version $relversion or later. You have $toolsver.\n";
       
   416     print "         It's recommended you stop and upgrade your tools before continuing, as\n";
       
   417     print "         the release probably won't install correctly.\n";
       
   418     print "         Continue? [y/n] ";
       
   419     my $response = <STDIN>;
       
   420     chomp $response;
       
   421     if (lc $response eq 'y') {
       
   422       return;
       
   423     }
       
   424     die "Aborting operation.\n";
       
   425   }
       
   426 }
       
   427 
       
   428 1;
       
   429 
       
   430 =head1 NAME
       
   431 
       
   432 RelData.pm - Provides an interface to data associated with a release.
       
   433 
       
   434 =head1 DESCRIPTION
       
   435 
       
   436 Along with the source and binaries of a component release, the following information is also stored:
       
   437 
       
   438 =over 4
       
   439 
       
   440 =item *
       
   441 
       
   442 The name of the F<mrp> file used to create the release.
       
   443 
       
   444 =item *
       
   445 
       
   446 The release's internal version.
       
   447 
       
   448 =item *
       
   449 
       
   450 The name and version of every component in the environment used to create the release.
       
   451 
       
   452 =item *
       
   453 
       
   454 The time and date the release was made.
       
   455 
       
   456 =item *
       
   457 
       
   458 The release notes source, which can subsequently be used to compile the release notes.
       
   459 
       
   460 =back
       
   461 
       
   462 All this information is stored in a single file named F<reldata> within the release directory using the module Data::Dumper.
       
   463 
       
   464 =head1 INTERFACE
       
   465 
       
   466 =head2 New
       
   467 
       
   468 Creates a new C<RelData> object and corresponding data file. Expects to be passed an C<IniData> reference, a component name, a version, an internal version,  an F<mrp> file name, release notes source file name, a reference to a list of components in the release environment and a verbosity level. This information is assembled into an in-memory data structure, and then written into F<reldata> in the component's release directory. You may optionally pass a "project" name to this function, to specify where the F<reldata> should be written.
       
   469 
       
   470 =head2 Open
       
   471 
       
   472 Creates a C<RelData> object from an already existing data file. Expects to be passed an C<IniData> reference, a component name, a version, and a verbosity level.
       
   473 
       
   474 =head2 OpenExternal
       
   475 
       
   476 As C<New> except expects to be explicitly passed an archive path file name, rather than an C<IniData> object. Effectively creates a C<RelData> object from an external archive.
       
   477 
       
   478 =head2 OpenSet
       
   479 
       
   480 Expects to be passed an C<IniData> reference, a component name, and a verbosity level. Opens C<RelData> objects for all of the releases of the specified component made to date and returns a reference to an array of references to them in descending date order.
       
   481 
       
   482 Optionally takes a regular expression to limit the versions that are returned.
       
   483 
       
   484 =head2 Component
       
   485 
       
   486 Returns the component name.
       
   487 
       
   488 =head2 Version
       
   489 
       
   490 Returns the component's version.
       
   491 
       
   492 =head2 InternalVersion
       
   493 
       
   494 Returns the component's internal version.
       
   495 
       
   496 =head2 MrpName
       
   497 
       
   498 Returns the component's F<mrp> file name.
       
   499 
       
   500 =head2 Environment
       
   501 
       
   502 Returns a reference to a hash containing component name / version pairs for the components that were in the release environment.
       
   503 
       
   504 =head2 NotesSource
       
   505 
       
   506 Returns a reference to a hash containing all the data needed to compile a set of release notes.
       
   507 
       
   508 =head1 SourceItems
       
   509 
       
   510 Returns a reference to a hash of all the "source" lines that were in the MRP file used to create this component. This function will die if no such information was found; this means it will die for releases created with Release Tools versions prior to 2.54.
       
   511 
       
   512 Note that a hash is used just to ensure uniqueness. Only the keys of the hash have value; the values of the hash currently have no meaning.
       
   513 
       
   514 =head2 SourceIncluded
       
   515 
       
   516 Returns a string version of the output of SourceItems.
       
   517 
       
   518 =head2 UpdateProject
       
   519 
       
   520 Expects to be passed a project. The project passed is then set as the project for the reldata.pm object, which is used when writing the reldata file.
       
   521 
       
   522 =head2 UpdateNotes
       
   523 
       
   524 Expects to be passed the name of a notes source file. Parses this and replaces the persisted version of the release notes.
       
   525 
       
   526 =head2 UpdateInternalVersion
       
   527 
       
   528 Expects to be passed an internal version. The internal version is then set as internal version for the reldata.pm object, which is used when writing the reldata file.
       
   529 
       
   530 =head2 UpdateEnv
       
   531 
       
   532 Expects to be passed an environment. The environment is then set as environment for the reldata.pm object, which is used when writing the reldata file.
       
   533 
       
   534 =head2 ReleaseTime
       
   535 
       
   536 Returns the time (in epoch seconds) at which the release was made.
       
   537 
       
   538 =head2 MadeWith
       
   539 
       
   540 Returns a string describing which tool this was made with, including the version number.
       
   541 
       
   542 =head2 EnvUserName
       
   543 
       
   544 Returns the full name of the user who made this release, according to environment variables FirstName and LastName.
       
   545 
       
   546 =head1 KNOWN BUGS
       
   547 
       
   548 None.
       
   549 
       
   550 =head1 COPYRIGHT
       
   551 
       
   552  Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
       
   553  All rights reserved.
       
   554  This component and the accompanying materials are made available
       
   555  under the terms of the License "Eclipse Public License v1.0"
       
   556  which accompanies this distribution, and is available
       
   557  at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
   558  
       
   559  Initial Contributors:
       
   560  Nokia Corporation - initial contribution.
       
   561  
       
   562  Contributors:
       
   563  
       
   564  Description:
       
   565  
       
   566 
       
   567 =cut