You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openoffice.apache.org by af...@apache.org on 2013/12/05 10:10:56 UTC

svn commit: r1548049 - in /openoffice/trunk/main: instsetoo_native/util/makefile.mk solenv/bin/modules/installer/patch/InstallationSet.pm solenv/bin/modules/installer/patch/ReleasesList.pm solenv/bin/patch_tool.pl

Author: af
Date: Thu Dec  5 09:10:55 2013
New Revision: 1548049

URL: http://svn.apache.org/r1548049
Log:
123531: patch_tool.pl can now extend releases.xml.

Modified:
    openoffice/trunk/main/instsetoo_native/util/makefile.mk
    openoffice/trunk/main/solenv/bin/modules/installer/patch/InstallationSet.pm
    openoffice/trunk/main/solenv/bin/modules/installer/patch/ReleasesList.pm
    openoffice/trunk/main/solenv/bin/patch_tool.pl

Modified: openoffice/trunk/main/instsetoo_native/util/makefile.mk
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/instsetoo_native/util/makefile.mk?rev=1548049&r1=1548048&r2=1548049&view=diff
==============================================================================
--- openoffice/trunk/main/instsetoo_native/util/makefile.mk (original)
+++ openoffice/trunk/main/instsetoo_native/util/makefile.mk Thu Dec  5 09:10:55 2013
@@ -75,6 +75,7 @@ help .PHONY :
 	@echo "experimental targets:"
 	@echo "    patch_create           create a patch for updating an installed office (Windows only)"
 	@echo "    patch_apply            apply a previously created patch"
+	@echo "    patch_update_releases_xml"
 	@echo 
 	@echo "Most targets (all except aoo_srcrelease and updatepack) accept suffixes"
 	@echo "    add _<language> to build a target for one language only"
@@ -307,14 +308,25 @@ $(BIN)$/dev$/intro.zip : $(SOLARCOMMONPC
 
 .IF "$(OS)" == "WNT"
 patch_create .PHONY : $(PRJ)$/data
-	perl -I $(SOLARENV)$/bin/modules $(SOLARENV)$/bin$/patch_create.pl	\
+	perl -I $(SOLARENV)$/bin/modules $(SOLARENV)$/bin$/patch_tool.pl	\
+		create								\
 		--product-name Apache_OpenOffice				\
 		--output-path $(OUT)						\
 		--data-path $(PRJ)$/data					\
 		--lst-file $(PRJ)$/util$/openoffice.lst
 patch_apply .PHONY :
-	perl -I $(SOLARENV)$/bin/modules $(SOLARENV)$/bin$/patch_apply.pl \
-		../wntmsci12.pro/Apache_OpenOffice/msp/v-4-0-1_v-4-1-0/en-US/openoffice.msp
+	perl -I $(SOLARENV)$/bin/modules $(SOLARENV)$/bin$/patch_tool.pl	\
+		apply								\
+		--product-name Apache_OpenOffice				\
+		--output-path $(OUT)						\
+		--lst-file $(PRJ)$/util$/openoffice.lst
+patch_update_releases_xml .PHONY:
+	perl -I $(SOLARENV)$/bin/modules $(SOLARENV)$/bin$/patch_tool.pl	\
+		update-releases-xml						\
+		--product-name Apache_OpenOffice				\
+		--output-path $(OUT)						\
+		--lst-file $(PRJ)$/util$/openoffice.lst\
+		--target-version 4.0.1
 
 $(PRJ)$/data :
 	mkdir $@

Modified: openoffice/trunk/main/solenv/bin/modules/installer/patch/InstallationSet.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/patch/InstallationSet.pm?rev=1548049&r1=1548048&r2=1548049&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/patch/InstallationSet.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/patch/InstallationSet.pm Thu Dec  5 09:10:55 2013
@@ -304,21 +304,27 @@ sub Download ($$$)
     $installer::logger::Info->printf("downloading %s\n", $basename);
     $installer::logger::Info->printf("    from '%s'\n", $location);
     my $filesize = $release_data->{'file-size'};
-    $installer::logger::Info->printf("    expected size is %d\n", $filesize);
-    my $temporary_filename = $filename . ".part";
-    my $resume_size = 0;
-    if ( -f $temporary_filename)
+    if (defined $filesize)
+    {
+        $installer::logger::Info->printf("    expected size is %d\n", $filesize);
+    }
+    else
     {
-        $resume_size = -s $temporary_filename;
-        $installer::logger::Info->printf(" trying to resume at %d/%d bytes\n", $resume_size, $filesize);
+        $installer::logger::Info->printf("    file size is not yet known\n");
     }
+    my $temporary_filename = $filename . ".part";
+    my $resume_size = 0;
     
     # Prepare checksum.
     my $checksum = undef;
     my $checksum_type = $release_data->{'checksum-type'};
     my $checksum_value = $release_data->{'checksum-value'};
     my $digest = undef;
-    if ($checksum_type eq "sha256")
+    if ( ! defined $checksum_value)
+    {
+        # No checksum available.  Skip test.
+    }
+    elsif ($checksum_type eq "sha256")
     {
         $digest = Digest->new("SHA-256");
     }
@@ -335,7 +341,7 @@ sub Download ($$$)
     }
 
     # Download the extension.
-    open my $out, ">>$temporary_filename";
+    open my $out, ">$temporary_filename";
     binmode($out);
 
     my $mode = $|;
@@ -359,21 +365,28 @@ sub Download ($$$)
             {
                 $last_was_redirect = 0;
                 # Throw away the data we got so far.
-                $digest->reset();
+                $digest->reset() if defined $digest;
                 close $out;
                 open $out, ">$temporary_filename";
                 binmode($out);
             }
             my($response,$agent,$h,$data)=@_;
             print $out $data;
-            $digest->add($data);
+            $digest->add($data) if defined $digest;
             $bytes_read += length($data);
-            printf("read %*d / %d  %d%%  \r",
-                length($filesize),
-                $bytes_read,
-                $filesize,
-                $bytes_read*100/$filesize);  
-        });
+            if (defined $filesize)
+            {
+                printf("read %*d / %d  %d%%  \r",
+                    length($filesize),
+                    $bytes_read,
+                    $filesize,
+                    $bytes_read*100/$filesize);
+            }
+            else
+            {
+                printf("read %6.2f MB\r", $bytes_read/(1024.0*1024.0));
+            }
+            });
     my $response;
     if ($resume_size > 0)
     {
@@ -393,9 +406,10 @@ sub Download ($$$)
     
     if ($response->is_success())
     {
-        if ($digest->hexdigest() eq $checksum_value)
+        if ( ! defined $digest
+            || $digest->hexdigest() eq $checksum_value)
         {
-            $installer::logger::Info->PrintInfo("download was successfull\n");
+            $installer::logger::Info->print("download was successfull\n");
             if ( ! rename($temporary_filename, $filename))
             {
                 installer::logger::PrintError("can not rename '%s' to '%s'\n", $temporary_filename, $filename);
@@ -453,7 +467,8 @@ sub ProvideDownloadSet ($$$)
     else
     {
         $installer::logger::Info->printf("download set exists at '%s'\n", $ext_sources_filename);
-        if ($release_item->{'checksum-type'} eq 'sha256')
+        if (defined $release_item->{'checksum-value'}
+            && $release_item->{'checksum-type'} eq 'sha256')
         {
             $installer::logger::Info->printf("checking SHA256 checksum\n");
             my $digest = Digest->new("SHA-256");

Modified: openoffice/trunk/main/solenv/bin/modules/installer/patch/ReleasesList.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/patch/ReleasesList.pm?rev=1548049&r1=1548048&r2=1548049&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/patch/ReleasesList.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/patch/ReleasesList.pm Thu Dec  5 09:10:55 2013
@@ -157,6 +157,17 @@ sub GetAttribute ($$)
 
 
 sub PrintNode($$);
+
+=head2 ReadDomTree ($filename)
+
+    Read the dom tree for the XML in $filename.
+
+    Note that
+    a) this was initially written for another XML library that provided the dom tree directly.
+    b) the dom tree creation is basic and simple but good enough for the current format.
+       When the format should change substantially, then we may need a better parser.
+
+=cut
 sub ReadDomTree ($)
 {
     my ($filename) = @_;
@@ -182,6 +193,81 @@ sub ReadDomTree ($)
 
 
 
+=head HandleStartTag ($data, $expat, $element_name, @attributes)
+
+    Callback for start tags.
+
+    A new hash is appended to the array that is referenced by the parent by $element_name.
+    That means that when this function ends there the new hash can be referenced by
+        my $parent = $data->{'node_stack'}->[-1];
+        my $new_hash = $parent->{$element_name}->[-1];
+
+    Note that, just like in other implementations of dom trees,
+    $parent->{$element_name} is an array, even when there is only one
+    element.
+
+    The new hash is empty or contains the given @attributes as hash.
+    When fully read (ie its end tag has been processed) then it can contain two special keys:
+    __attributes__ for the attributes
+    __text__ for the concatenated text parts
+
+=cut
+sub HandleStartTag ($$$@)
+{
+    my ($data, $expat, $element_name, @attributes) = @_;
+
+    # Create new node with attributes.
+    my $node = {'__attributes__' => {@attributes}};
+
+    # Append it to the list of $element_name objects.
+    my $current_node = $data->{'current_node'};
+    $current_node->{$element_name} = [] unless defined $current_node->{$element_name};
+    push @{$current_node->{$element_name}}, $node;
+
+    # Make the new node the current node.
+    push @{$data->{'node_stack'}}, $current_node;
+    $data->{'current_node'} = $node;
+}
+
+=head HandleEndTag ($data, $expat, $element_name, @attributes)
+
+    Callback for end tags.
+
+=cut
+sub HandleEndTag ($$$)
+{
+    my ($data, $expat, $element) = @_;
+
+    # Restore the parent node as current node.
+    $data->{'current_node'} = pop @{$data->{'node_stack'}};
+}
+
+=head2 HandleText ($data, $expat, $text)
+
+    Callback for text.
+
+    $text is appended to the __text__ member of the current node in
+    the dom tree.
+
+=cut
+sub HandleText ($$$)
+{
+    my ($data, $expat, $text) = @_;
+    if ($text !~ /^\s*$/)
+    {
+        $data->{'current_node'}->{'__text__'} .= $text;
+    }
+}
+
+
+
+
+=head2 PrintNode ($indentation, $node)
+
+    For debugging.
+    Print $node recursively with initial $indentation.
+
+=cut
 sub PrintNode($$)
 {
     my ($indentation, $node) = @_;
@@ -223,39 +309,7 @@ sub PrintNode($$)
 }
 
 
-sub HandleStartTag ($$$@)
-{
-    my ($data, $expat, $element, @attributes) = @_;
 
-    # Create new node with attributes.
-    my $node = {'__attributes__' => {@attributes}};
-
-    # Append it to the list of $element objects.
-    my $current_node = $data->{'current_node'};
-    $current_node->{$element} = [] unless defined $current_node->{$element};
-    push @{$current_node->{$element}}, $node;
-
-    # Make the new node the current node.
-    push @{$data->{'node_stack'}}, $current_node;
-    $data->{'current_node'} = $node;
-}
-
-sub HandleEndTag ($$$)
-{
-    my ($data, $expat, $element) = @_;
-
-    # Restore the parent node as current node.
-    $data->{'current_node'} = pop @{$data->{'node_stack'}};
-}
-
-sub HandleText ($$$)
-{
-    my ($data, $expat, $text) = @_;
-    if ($text !~ /^\s*$/)
-    {
-        $data->{'current_node'}->{'__text__'} .= $text;
-    }
-}
 
 =head2 Read($self, $filename)
 
@@ -287,22 +341,26 @@ sub Read ($$)
         
         $self->{$version}->{$package_format}->{'upgrade-code'} = $upgrade_code;
         $self->{$version}->{$package_format}->{'build-id'} = $build_id;
+        $self->{$version}->{$package_format}->{'url-template'} = $url_template;
 
+        my @languages = ();
         foreach my $item_node (@{$download_node->{'item'}})
         {
             my ($language, $download_data) = ParseDownloadData($item_node, $url_template);
             if (defined $download_data && defined $language)
             {
+                push @languages, $language;
                 $self->{$version}->{$package_format}->{$language} = $download_data;
             }
         }
+        $self->{$version}->{$package_format}->{'languages'} = \@languages;
     }
 }
 
 
 
 
-=head2 ParseDownloadData ($download_node)
+=head2 ParseDownloadData ($item_node, $url_template)
 
     Parse the data for one set of download data (there is one per release and package format).
 
@@ -339,6 +397,119 @@ sub ParseDownloadData ($$)
 
 
 
+=head2 Write($self, $filename)
+
+    Write the content of the releases data to a file named $filename.
+
+=cut
+sub Write ($$)
+{
+    my ($self, $filename) = @_;
+
+    open my $out, ">", $filename || die "can not write releases data to ".$filename;
+    $self->WriteHeader($out);
+    $self->WriteContent($out);
+    close $out;
+}
+
+
+
+
+=head2 WriteContent ($self, $out)
+
+    Write the content of the releases.xml list.
+
+=cut
+sub WriteContent ($$)
+{
+    my ($self, $out) = @_;
+    
+    print $out "<releases>\n";
+    # Write the data sets for each releases with the same sort order as @{$self->{'releases'}}
+    foreach my $version (@{$self->{'releases'}})
+    {
+        print $out "  <release>\n";
+
+        my @version_array = split(/\./, $version);
+        printf $out "    <version>\n";
+        printf $out "      <major>%s</major>\n", $version_array[0];
+        printf $out "      <minor>%s</minor>\n", $version_array[1];
+        printf $out "      <micro>%s</micro>\n", $version_array[2];
+        printf $out "    </version>\n";
+
+        # Write one download data set per package format.
+        while (my ($package_format, $data) = each %{$self->{$version}})
+        {
+            print $out "    <download>\n";
+            printf $out "      <package-format>%s</package-format>\n", $package_format;
+            print $out "      <url-template>\n";
+            printf $out "        %s\n", $data->{'url-template'};
+            print $out "      </url-template>\n";
+            printf $out "      <upgrade-code>%s</upgrade-code>\n", $data->{'upgrade-code'};
+            printf $out "      <build-id>%s</build-id>\n", $data->{'build-id'};
+
+            foreach my $language (@{$data->{'languages'}})
+            {
+                my $language_data = $data->{$language};
+                print $out "      <item>\n";
+                printf $out "        <language>%s</language>\n", $language;
+                printf $out "        <checksum type=\"%s\">%s</checksum>\n",
+                    $language_data->{'checksum-type'},
+                    $language_data->{'checksum-value'};
+                printf $out "        <size>%s</size>\n", $language_data->{'file-size'};
+                printf $out "        <product-code>%s</product-code>\n", $language_data->{'product-code'};
+                print $out "      </item>\n";
+            }
+
+            print $out "    </download>\n";
+        }
+
+        print $out "    </release>\n";
+    }
+
+    print $out "</releases>\n";
+}
+
+
+
+
+=head2 WriteHeader ($self, $out)
+
+    Write the header for the releases.xml list.
+
+=cut
+sub WriteHeader ($$)
+{
+    my ($self, $out) = @_;
+    
+print $out <<EOT;
+<?xml version='1.0' encoding='UTF-8'?>
+<!--***********************************************************
+ * 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * 
+ ***********************************************************-->
+EOT
+}
+
+
+
+
 =head2 GetPreviousVersion($version)
 
     Look up $version in the sorted list of released versions.  Return

Modified: openoffice/trunk/main/solenv/bin/patch_tool.pl
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/patch_tool.pl?rev=1548049&r1=1548048&r2=1548049&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/patch_tool.pl (original)
+++ openoffice/trunk/main/solenv/bin/patch_tool.pl Thu Dec  5 09:10:55 2013
@@ -255,7 +255,10 @@ sub ProvideSourceInstallationSet ($$$)
         }
         else
         {
-            return 0 if ! installer::patch::Download($language, $release_data, $location, $basename, $filename);
+            return 0 if ! installer::patch::InstallationSet::Download(
+                $language,
+                $release_data,
+                $filename);
             return 0 if ! -f $filename;
         }
 
@@ -1121,6 +1124,12 @@ sub SetupPropertiesTable ($$)
     $table->SetRow(
         "*Name", "SEQUENCE_DATA_GENERATION_DISABLED",
         "Value", 1);
+
+    # We don't provide file size and hash values.
+    # This value is set to make this fact explicit (0 should be the default). 
+    $table->SetRow(
+        "*Name", "TrustMsi",
+        "Value", 0);
 }
 
 
@@ -1357,6 +1366,19 @@ sub CreateMsp ($)
 
 
 
+
+=head CreatePatch($context, $variables)
+
+    Create MSP patch files for all relevant languages.
+    The different steps are:
+    1. Determine the set of languages for which both the source and target installation sets are present.
+    Per language:
+        2. Unpack CAB files (for source and target).
+        3. Check if source and target releases are compatible.
+        4. Create the PCP driver file.
+        5. Create the MSP patch file.
+
+=cut
 sub CreatePatch ($$)
 {
     my ($context, $variables) = @_;
@@ -1377,7 +1399,7 @@ sub CreatePatch ($$)
         ->{$context->{'source-version'}}
         ->{$context->{'package-format'}};
 
-    # Create a patch for each language.
+    # 1. Determine the set of languages for which we can create patches.
     my @requested_languages = GetLanguages();
     my @valid_languages = FindValidLanguages($context, $release_data, \@requested_languages);
     $installer::logger::Info->printf("of the requested languages '%s' are valid: '%s'\n",
@@ -1388,7 +1410,7 @@ sub CreatePatch ($$)
         $installer::logger::Info->printf("processing language '%s'\n", $language);
         $installer::logger::Info->increase_indentation();
 
-        # Provide .msi and .cab files and unpacke .cab for the source release.
+        # 2a. Provide .msi and .cab files and unpacke .cab for the source release.
         $installer::logger::Info->printf("locating source package (%s)\n", $context->{'source-version'});
         $installer::logger::Info->increase_indentation();
         if ( ! installer::patch::InstallationSet::ProvideUnpackedCab(
@@ -1409,7 +1431,7 @@ sub CreatePatch ($$)
 
         $installer::logger::Info->decrease_indentation();
 
-        # Provide .msi and .cab files and unpacke .cab for the target release.
+        # 2b. Provide .msi and .cab files and unpacke .cab for the target release.
         $installer::logger::Info->printf("locating target package (%s)\n", $context->{'target-version'});
         $installer::logger::Info->increase_indentation();
         if ( ! installer::patch::InstallationSet::ProvideUnpackedCab(
@@ -1438,7 +1460,7 @@ sub CreatePatch ($$)
             $installer::logger::Info->printf("read %s table (source and target\n", $table_name);
         }
         
-        # Check if the source and target msis fullfil all necessary requirements.
+        # 3. Check if the source and target msis fullfil all necessary requirements.
         if ( ! Check($source_msi, $target_msi, $variables, $context->{'product-name'}))
         {
             $installer::logger::Info->printf("Error: Source and target releases are not compatible.\n");
@@ -1467,7 +1489,7 @@ sub CreatePatch ($$)
             );
         File::Path::make_path($msp_path) unless -d $msp_path;
 
-        # Create the .pcp file that drives the msimsp.exe command.
+        # 4. Create the .pcp file that drives the msimsp.exe command.
         my $pcp = CreatePcp(
             $source_msi,
             $target_msi,
@@ -1475,12 +1497,9 @@ sub CreatePatch ($$)
             $context,
             $msp_path,
             $pcp_schema_filename,
-            "Properties/Name:DontRemoveTempFolderWhenFinished" => "Value:1",
-            "Properties/Name:SEQUENCE_DATA_GENERATION_DISABLED" => "Value:1",
-            "Properties/Name:TrustMsi" => "Value:0",
-            "Properties/Name:ProductName" => "Value:OOO341");
+            "Properties/Name:DontRemoveTempFolderWhenFinished" => "Value:1");
 
-        # Finally create the msp.
+        # 5. Finally create the msp.
         CreateMsp($pcp);
 
         $installer::logger::Info->decrease_indentation();
@@ -1489,7 +1508,12 @@ sub CreatePatch ($$)
 
 
 
+=cut ApplyPatch ($context, $variables)
+
+    This is for testing only.
+    The patch is applied and (extensive) log information is created and transformed into HTML format.
 
+=cut
 sub ApplyPatch ($$)
 {
     my ($context, $variables) = @_;
@@ -1547,10 +1571,244 @@ sub ApplyPatch ($$)
 
 
 
+=head2 DownloadFile ($url)
+
+    A simpler version of InstallationSet::Download().  It is simple because it is used to
+    setup the $release_data structure that is used by InstallationSet::Download().
+
+=cut
+sub DownloadFile ($)
+{
+    my ($url) = shift;
+
+    my $agent = LWP::UserAgent->new();
+    $agent->timeout(120);
+    $agent->show_progress(0);
+
+    my $file_content = "";
+    my $last_was_redirect = 0;
+    my $bytes_read = 0;
+    $agent->add_handler('response_redirect'
+        => sub{
+            $last_was_redirect = 1;
+            return;
+        });
+    $agent->add_handler('response_data'
+        => sub{
+            if ($last_was_redirect)
+            {
+                $last_was_redirect = 0;
+                # Throw away the data we got so far.
+		$file_content = "";
+            }
+            my($response,$agent,$h,$data)=@_;
+	    $file_content .= $data;
+        });
+    $agent->get($url);
+
+    return $file_content;
+}
+
+
+
+
+sub CreateReleaseItem ($$$)
+{
+    my ($language, $exe_filename, $msi) = @_;
+
+    die "can not open installation set at ".$exe_filename unless -f $exe_filename;
+    
+    open my $in, "<", $exe_filename;
+    my $sha256_checksum = new Digest("SHA-256")->addfile($in)->hexdigest();
+    close $in;
+
+    my $filesize = -s $exe_filename;
+
+    # Get the product code property from the msi and strip the enclosing braces.
+    my $product_code = $msi->GetTable("Property")->GetValue("Property", "ProductCode", "Value");
+    $product_code =~ s/(^{|}$)//g;
+    my $upgrade_code = $msi->GetTable("Property")->GetValue("Property", "UpgradeCode", "Value");
+    $upgrade_code =~ s/(^{|}$)//g;
+    my $build_id = $msi->GetTable("Property")->GetValue("Property", "PRODUCTBUILDID", "Value");
+    
+    return {
+        'language' => $language,
+        'checksum-type' => "sha256",
+        'checksum-value' => $sha256_checksum,
+        'file-size' => $filesize,
+        'product-code' => $product_code,
+        'upgrade-code' => $upgrade_code,
+        'build-id' => $build_id
+    };
+}
+
+
+
+
+sub GetReleaseItemForCurrentBuild ($$$)
+{
+    my ($context, $language, $exe_basename) = @_;
+
+    # Target version is the current version.
+    # Search instsetoo_native for the installation set.
+    my $filename = File::Spec->catfile(
+        $context->{'output-path'},
+        $context->{'product-name'},
+        $context->{'package-format'},
+        "install",
+        $language."_download",
+        $exe_basename);
+    
+    printf("        current : %s\n", $filename);
+    if ( ! -f $filename)
+    {
+        printf("ERROR: can not find %s\n", $filename);
+        return undef;
+    }
+    else
+    {
+        my $msi = installer::patch::Msi->FindAndCreate(
+            $context->{'target-version'},
+            1,
+            $language,
+            $context->{'product-name'});
+        return CreateReleaseItem($language, $filename, $msi);
+    }
+}
+
+
+
+sub GetReleaseItemForOldBuild ($$$$)
+{
+    my ($context, $language, $exe_basename, $url_template) = @_;
+
+    # Use ext_sources/ as local cache for archive.apache.org
+    # and search these for the installation set.
+
+    my $version = $context->{'target-version'};
+    my $package_format =  $context->{'package-format'};
+    my $releases_list = installer::patch::ReleasesList::Instance();
+
+    my $url = $url_template;
+    $url =~ s/%L/$language/g;
+    $releases_list->{$version}->{$package_format}->{$language}->{'URL'} = $url;
+    
+    if ( ! installer::patch::InstallationSet::ProvideUnpackedExe(
+               $version,
+               0,
+               $language,
+               $package_format,
+               $context->{'product-name'}))
+    {
+        # Can not provide unpacked EXE.
+        return undef;
+    }
+    else
+    {
+        my $exe_filename = File::Spec->catfile(
+            $ENV{'TARFILE_LOCATION'},
+            $exe_basename);
+        my $msi = installer::patch::Msi->FindAndCreate(
+            $version,
+            0,
+            $language,
+            $context->{'product-name'});
+        return CreateReleaseItem($language, $exe_filename, $msi);
+    }
+}
+
+
+
+
+sub UpdateReleasesXML($$)
+{
+    my ($context, $variables) = @_;
+
+    my $releases_list = installer::patch::ReleasesList::Instance();
+    my $output_filename = File::Spec->catfile(
+        $context->{'output-path'},
+        "misc",
+        "releases.xml");
+
+    my $target_version = $context->{'target-version'};
+    my %version_hash = map {$_=>1} @{$releases_list->{'releases'}};
+    my $item_hash = undef;
+    if ( ! defined $version_hash{$context->{'target-version'}})
+    {
+        # Target version is not yet present.  Add it and print message that asks caller to check order.
+        push @{$releases_list->{'releases'}}, $target_version;
+        printf("adding data for new version %s to list of released versions.\n", $target_version);
+        printf("please check order of releases in $output_filename\n");
+        $item_hash = {};
+    }
+    else
+    {
+        printf("adding data for existing version %s to releases.xml\n", $target_version);
+        $item_hash = $releases_list->{$target_version}->{$context->{'package-format'}};
+    }
+    $releases_list->{$target_version} = {$context->{'package-format'} => $item_hash};
+    
+    my @languages = GetLanguages();
+    my %language_items = ();
+    foreach my $language (@languages)
+    {
+        # There are three different sources where to find the downloadable installation sets.
+        # 1. archive.apache.org for previously released versions.
+        # 2. A local cache or repository directory that conceptually is a local copy of archive.apache.org
+        # 3. The downloadable installation sets built in instsetoo_native/.
+
+        my $exe_basename = sprintf(
+            "%s_%s_Win_x86_install_%s.exe",
+            $context->{'product-name'},
+            $target_version,
+            $language);
+        my $url_template = sprintf(
+            "http://archive.apache.org/dist/openoffice/%s/binaries/%%L/%s_%s_Win_x86_install_%%L.exe",
+            $target_version,
+            $context->{'product-name'},
+            $target_version);
+
+        my $item = undef;
+        if ($target_version eq $variables->{PRODUCTVERSION})
+        {
+            $item = GetReleaseItemForCurrentBuild($context, $language, $exe_basename);
+        }
+        else
+        {
+            $item = GetReleaseItemForOldBuild($context, $language, $exe_basename, $url_template);
+        }
+        
+        next unless defined $item;
+        
+        $language_items{$language} = $item;
+        $item_hash->{$language} = $item;
+        $item_hash->{'upgrade-code'} = $item->{'upgrade-code'};
+        $item_hash->{'build-id'} = $item->{'build-id'};
+        $item_hash->{'url-template'} = $url_template;
+    }
+    
+    my @valid_languages = sort keys %language_items;
+    $item_hash->{'languages'} = \@valid_languages;
+
+    $releases_list->Write($output_filename);
+
+    printf("\n\n");
+    printf("please copy '%s' to main/instsetoo_native/data\n", $output_filename);
+    printf("and check in the modified file to the version control system\n");
+}
+
+
+
+
 sub main ()
 {
     installer::logger::SetupSimpleLogging(undef);
     my $context = ProcessCommandline();
+    die "ERROR: list file is not defined, please use --lst-file option"
+        unless defined $context->{'lst-file'};
+    die "ERROR: product name is not defined, please use --product-name option"
+        unless defined $context->{'product-name'};
+
     my ($variables, undef, undef) = installer::ziplist::read_openoffice_lst_file(
         $context->{'lst-file'},
         $context->{'product-name'},
@@ -1565,6 +1823,10 @@ sub main ()
     {
         ApplyPatch($context, $variables);
     }
+    elsif ($context->{'command'} eq "update-releases-xml")
+    {
+        UpdateReleasesXML($context, $variables);
+    }
 }