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/13 13:05:16 UTC

svn commit: r1550699 [2/2] - in /openoffice/trunk/main: instsetoo_native/util/ solenv/bin/ solenv/bin/modules/installer/ solenv/bin/modules/installer/patch/ solenv/bin/modules/installer/windows/

Modified: openoffice/trunk/main/solenv/bin/patch_tool.pl
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/patch_tool.pl?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/patch_tool.pl (original)
+++ openoffice/trunk/main/solenv/bin/patch_tool.pl Fri Dec 13 12:05:15 2013
@@ -38,6 +38,8 @@ use installer::patch::Msi;
 use installer::patch::ReleasesList;
 use installer::patch::Version;
 
+#use Carp::Always;
+
 use strict;
 
 
@@ -64,6 +66,10 @@ use strict;
              The version that is to be patched.
         --target-version <major>.<minor>.<micro>
              The version after the patch has been applied.
+        --language <language-code>
+             Language of the installation sets.
+        --package-format
+             Only the package format 'msi' is supported at the moment.
 
 =head1 DESCRIPTION
 
@@ -80,7 +86,6 @@ use strict;
 
 =cut
 
-#    my $ImageFamily = "MNPapps";
 # The ImageFamily name has to have 1-8 alphanumeric characters.
 my $ImageFamily = "AOO"; 
 my $SourceImageName = "Source";
@@ -90,21 +95,26 @@ my $TargetImageName = "Target";
 
 sub ProcessCommandline ()
 {
-    my $arguments = {
+    my $context = {
         'product-name' => undef,
         'output-path' => undef,
         'data-path' => undef,
         'lst-file' => undef,
         'source-version' => undef,
-        'target-version' => undef};
+        'target-version' => undef,
+        'language' => undef,
+        'package-format' => undef
+    };
 
     if ( ! GetOptions(
-               "product-name=s", \$arguments->{'product-name'},
-               "output-path=s", \$arguments->{'output-path'},
-               "data-path=s" => \$arguments->{'data-path'},
-               "lst-file=s" => \$arguments->{'lst-file'},
-               "source-version:s" => \$arguments->{'source-version'},
-               "target-version:s" => \$arguments->{'target-version'}
+               "product-name=s", \$context->{'product-name'},
+               "output-path=s", \$context->{'output-path'},
+               "data-path=s" => \$context->{'data-path'},
+               "lst-file=s" => \$context->{'lst-file'},
+               "source-version:s" => \$context->{'source-version'},
+               "target-version:s" => \$context->{'target-version'},
+               "language=s" => \$context->{'language'},
+               "package-format=s" => \$context->{'package-format'}
         ))
     {
         pod2usage(2);
@@ -112,14 +122,9 @@ sub ProcessCommandline ()
 
     # Only the command should be left in @ARGV.
     pod2usage(2) unless scalar @ARGV == 1;
-    $arguments->{'command'} = shift @ARGV;
-    
-    # At the moment we only support patches on windows.  When this
-    # is extended in the future we need the package format as an
-    # argument.
-    $arguments->{'package-format'} = "msi";
+    $context->{'command'} = shift @ARGV;
 
-    return $arguments;
+    return $context;
 }
 
 
@@ -181,46 +186,28 @@ sub ProvideInstallationSets ($$)
 
 
 
-sub GetLanguages ()
+sub IsLanguageValid ($$$)
 {
-    # The set of languages is taken from the WITH_LANG environment variable.
-    # If that is missing or is empty then the default 'en-US' is used instead.
-    my @languages = ("en-US");
-    my $with_lang = $ENV{'WITH_LANG'};
-    if (defined $with_lang && $with_lang ne "")
-    {
-        @languages = split(/\s+/, $with_lang);
-    }
-    return @languages;
-}
-
-
+    my ($context, $release_data, $language) = @_;
 
+    my $normalized_language = installer::languages::get_normalized_language($language);
 
-sub FindValidLanguages ($$$)
-{
-    my ($context, $release_data, $languages) = @_;
-
-    my @valid_languages = ();
-    foreach my $language (@$languages)
+    if ( ! ProvideInstallationSets($context, $language))
     {
-        if ( ! ProvideInstallationSets($context, $language))
-        {
-            installer::logger::PrintError("    '%s' has no target installation set\n", $language);
-        }
-        elsif ( ! defined $release_data->{$language})
-        {
-            installer::logger::PrintError("    '%s' is not a released language for version %s\n",
-                $language,
-                $context->{'source-version'});
-        }
-        else
-        {
-            push @valid_languages, $language;
-        }
+        installer::logger::PrintError("    '%s' has no target installation set\n", $language);
+        return 0;
+    }
+    elsif ( ! defined $release_data->{$normalized_language})
+    {
+        installer::logger::PrintError("    '%s' is not a released language for version %s\n",
+            $language,
+            $context->{'source-version'});
+        return 0;
+    }
+    else
+    {
+        return 1;
     }
-
-    return @valid_languages;
 }
 
 
@@ -327,6 +314,19 @@ sub DetermineVersions ($$)
         }
         $context->{'source-version'} = $last_release;
     }
+
+    if (defined $context->{'source-version'})
+    {
+        $context->{'source-version-dash'} = installer::patch::Version::ArrayToDirectoryName(
+            installer::patch::Version::StringToNumberArray(
+                $context->{'source-version'}));
+    }
+    if (defined $context->{'target-version'})
+    {
+        $context->{'target-version-dash'} = installer::patch::Version::ArrayToDirectoryName(
+            installer::patch::Version::StringToNumberArray(
+                $context->{'target-version'}));
+    }
 }
 
 
@@ -352,7 +352,7 @@ sub CheckUpgradeCode($$)
     }
     else
     {
-        $installer::logger::Info->printf("OK: UpgradeCode values are identical\n");
+        $installer::logger::Info->printf("OK: UpgradeCode values are different\n");
         return 1;
     }
 }
@@ -382,7 +382,7 @@ sub CheckProductCode($$)
     }
     else
     {
-        $installer::logger::Info->printf("OK: ProductCode properties differ\n");
+        $installer::logger::Info->printf("OK: ProductCodes are identical\n");
         return 1;
     }
 }
@@ -501,25 +501,60 @@ sub CheckNewFiles($$)
 
     # Create data structures for fast lookup.
     my %source_file_map = map {$_->GetValue("File") => $_} @{$source_file_table->GetAllRows()};
-    my @target_files = map {$_->GetValue("File")} @{$target_file_table->GetAllRows()};
+    my %target_files_map = map {$_->GetValue("File") => $_} @{$target_file_table->GetAllRows()};
 
     # Search for added files (files in target that where not in source).
-    my $added_file_count = 0;
-    foreach my $uniquename (@target_files)
+    my @added_files = ();
+    foreach my $uniquename (keys %target_files_map)
     {
         if ( ! defined $source_file_map{$uniquename})
         {
-            ++$added_file_count;
+            push @added_files, $target_files_map{$uniquename};
         }
     }
 
-    if ($added_file_count > 0)
+    if (scalar @added_files > 0)
     {
-        $installer::logger::Info->printf("Warning: %d files have been added\n", $added_file_count);
+        $installer::logger::Info->printf("Warning: %d files have been added\n", scalar @added_files);
 
-        $installer::logger::Info->printf("Check for new files being part of new components is not yet implemented\n");
-        
-        return 1;
+        # Prepare component tables and hashes.
+        my $source_component_table = $source_msi->GetTable("Component");
+        my $target_component_table = $target_msi->GetTable("Component");
+        die unless defined $source_component_table && defined $target_component_table;
+        my %source_component_map = map {$_->GetValue('Component') => $_} @{$source_component_table->GetAllRows()};
+        my %target_component_map = map {$_->GetValue('Component') => $_} @{$target_component_table->GetAllRows()};
+
+        my @new_files_with_existing_components = ();
+        foreach my $target_file_row (@added_files)
+        {
+	    $installer::logger::Info->printf("    %s (%s)\n",
+		$target_file_row->GetValue("FileName"),
+		$target_file_row->GetValue("File"));
+
+            # Get target component for target file.
+            my $target_component = $target_file_row->GetValue('Component_');
+
+            # Check that the component is not part of the source components.
+            if (defined $source_component_map{$target_component})
+            {
+                push @new_files_with_existing_components, $target_file_row;
+            }
+        }
+
+        if (scalar @new_files_with_existing_components > 0)
+        {
+            $installer::logger::Info->printf(
+                "Error: %d new files have existing components (which must also be new)\n",
+                scalar @new_files_with_existing_components);
+            return 0;
+        }
+        else
+        {
+            $installer::logger::Info->printf(
+                "OK: all %d new files also have new components\n",
+		scalar @added_files);
+            return 1;
+        }
     }
     else
     {
@@ -531,13 +566,96 @@ sub CheckNewFiles($$)
 
 
 
-=head2 CheckComponentSets($source_msi, $target_msi)
+=head2 CheckFeatureSets($source_msi, $target_msi)
+
+    Features must not be removed but can be added.
+    Parent features of new features also have to be new.
+
+=cut
+sub CheckFeatureSets($$)
+{
+    my ($source_msi, $target_msi) = @_;
+
+    # Get the 'Feature' tables.
+    my $source_feature_table = $source_msi->GetTable("Feature");
+    my $target_feature_table = $target_msi->GetTable("Feature");
+
+    # Create data structures for fast lookup.
+    my %source_feature_map = map {$_->GetValue("Feature") => $_} @{$source_feature_table->GetAllRows()};
+    my %target_feature_map = map {$_->GetValue("Feature") => $_} @{$target_feature_table->GetAllRows()};
+
+    # Check that no feature has been removed.
+    my @removed_features = ();
+    foreach my $feature_name (keys %source_feature_map)
+    {
+        if ( ! defined $target_feature_map{$feature_name})
+        {
+            push @removed_features, $feature_name;
+        }
+    }
+    if (scalar @removed_features > 0)
+    {
+        # There are removed features.
+        $installer::logger::Info->printf(
+            "Error: %d features have been removed:\n",
+            scalar @removed_features);
+        $installer::logger::Info->printf("       %s\n", join(", ", @removed_features));
+        return 0;
+    }
+
+    # Check that added features belong to new parent features.
+    my @added_features = ();
+    foreach my $feature_name (keys %target_feature_map)
+    {
+        if ( ! defined $source_feature_map{$feature_name})
+        {
+            push @added_features, $feature_name;
+        }
+    }
+
+    if (scalar @added_features > 0)
+    {
+        $installer::logger::Info->printf("Warning: %d features have been addded\n", scalar @added_features);
+
+        my @new_features_with_existing_parents = ();
+        foreach my $new_feature (@added_features)
+        {
+            my $target_feature = $target_feature_map{$new_feature};
+            if (defined $source_feature_map{$target_feature->{'Feature_Parent'}})
+            {
+                push @new_features_with_existing_parents, $target_feature;
+            }
+        }
+
+        if (scalar @new_features_with_existing_parents > 0)
+        {
+            $installer::logger::Info->printf(
+                "Error: %d new features have existing parents (which also must be new)\n",
+                scalar @new_features_with_existing_parents);
+            return 0;
+        }
+        else
+        {
+            $installer::logger::Info->printf(
+                "OK: parents of all new features are also new\n");
+            return 1;
+        }
+    }
+
+    $installer::logger::Info->printf("OK: feature sets in source and target are compatible\n");
+    return 1;
+}
+
+
+
+
+=head2 CheckRemovedComponents($source_msi, $target_msi)
 
     Components must not be removed but can be added.
     Features of added components have also to be new.
 
 =cut
-sub CheckComponentSets($$)
+sub CheckRemovedComponents ($$)
 {
     my ($source_msi, $target_msi) = @_;
 
@@ -558,7 +676,12 @@ sub CheckComponentSets($$)
             push @removed_components, $componentname;
         }
     }
-    if (scalar @removed_components > 0)
+    if (scalar @removed_components == 0)
+    {
+	$installer::logger::Info->printf("OK: no removed components\n");
+	return 1;
+    }
+    else
     {
         # There are removed components.
 
@@ -587,48 +710,115 @@ sub CheckComponentSets($$)
             return 0;
         }
     }
+}
+
+
+
+
+sub GetTableAndMap ($$$)
+{
+    my ($msi, $table_name, $index_column) = @_;
+
+    my $table = $msi->GetTable($table_name);
+    my %map = map {$_->GetValue($index_column) => $_} @{$table->GetAllRows()};
+
+    return ($table, \%map);
+}
+
+
+=head2 CheckAddedComponents($source_msi, $target_msi)
+
+    Components can be added.
+    Features of added components have also to be new.
+
+=cut
+sub CheckAddedComponents ($$)
+{
+    my ($source_msi, $target_msi) = @_;
+    
+    # Get the 'Component' tables and maps.
+    my ($source_component_table, $source_component_map)
+	= GetTableAndMap($source_msi, "Component", "Component");
+    my ($target_component_table, $target_component_map)
+	= GetTableAndMap($target_msi, "Component", "Component");
 
     # Check that added components belong to new features.
     my @added_components = ();
-    foreach my $componentname (keys %target_component_map)
+    foreach my $componentname (keys %$target_component_map)
     {
-        if ( ! defined $source_component_map{$componentname})
+        if ( ! defined $source_component_map->{$componentname})
         {
             push @added_components, $componentname;
         }
     }
 
-    if (scalar @added_components > 0)
+    if (scalar @added_components == 0)
     {
-        # Check if any of them is not a registry component.
-        my $is_file_component_removed = 0;
-        foreach my $componentname (@removed_components)
-        {
-            if ($componentname !~ /^registry/)
-            {
-                $is_file_component_removed = 1;
-            }
-        }
-
-        if ($is_file_component_removed)
-        {
-            $installer::logger::Info->printf(
-                "Warning: %d components have been addded\n",
-                scalar @added_components);
-            $installer::logger::Info->printf(
-                "Test for new components belonging to new features has not yet been implemented\n");
-            return 0;
-        }
-        else
-        {
-            $installer::logger::Info->printf(
-                "Warning: %d components have been addded, all of them registry components\n",
-                scalar @added_components);
-        }
+	$installer::logger::Info->printf("OK: no new components\n");
+	return 1;
     }
+    else
+    {
+	$installer::logger::Info->printf(
+	    "Warning: %d components have been addded\n",
+	    scalar @added_components);
+
+        # Check that the referencing features are also new.
+	my $target_feature_component_table = $target_msi->GetTable("FeatureComponents");
+
+	my $error = 0;
+        foreach my $component_name (@added_components)
+        {
+	    my @feature_names = ();
+	    foreach my $feature_component_row (@{$target_feature_component_table->GetAllRows()})
+	    {
+		if ($feature_component_row->GetValue("Component_") eq $component_name)
+		{
+		    my $feature_name = $feature_component_row->GetValue("Feature_");
+		    push @feature_names, $feature_name;
+		}
+	    }
+	    if (scalar @feature_names == 0)
+	    {
+		$installer::logger::Info->printf("Error: no feature found for component '%s'\n", $component_name);
+		$error = 1;		
+	    }
+	    else
+	    {
+		# Check that the referenced features are new and have new parents (if they have parents).
+		my ($source_feature_table, $source_feature_map)
+		    = GetTableAndMap($source_msi, "Feature", "Feature");
+		my ($target_feature_table, $target_feature_map)
+		    = GetTableAndMap($target_msi, "Feature", "Feature");
+		foreach my $feature_name (@feature_names)
+		{
+		    $installer::logger::Info->printf("    component '%s' -> feature '%s'\n",
+			$component_name,
+			$feature_name);
+		    my $source_feature_row = $source_feature_map->{$feature_name};
+		    if (defined $source_feature_row)
+		    {
+			$installer::logger::Info->printf("Warning(Error?): feature of new component is not new\n");
+			$error = 1;
+		    }
+		    else
+		    {
+			# Feature is new. Check that the parent feature is also new.
+			my $target_feature_row = $target_feature_map->{$feature_name};
+			my $parent_feature_name = $target_feature_row->GetValue("Feature_Parent");
+			if ($parent_feature_name ne "" && defined $source_feature_map->{$parent_feature_name})
+			{
+			    $installer::logger::Info->printf("Warning(Error?): parent feature of new component is not new\n");
+			    $error = 1;
+			}
+		    }
+		}
+	    }
+	}
 
-    $installer::logger::Info->printf("OK: component sets in source and target are compatible\n");
-    return 1;
+#	return !$error;
+	return 1;
+    }
 }
 
 
@@ -962,6 +1152,131 @@ sub CheckComponentKeyPath ($$)
 
 
 
+sub GetMissingReferences ($$$$$)
+{
+    my ($table, $key, $map, $what, $report_key) = @_;
+
+    my @missing_references = ();
+    
+    foreach my $row (@{$table->GetAllRows()})
+    {
+        my $value = $row->GetValue($key);
+        if ($value ne "" && ! defined $map->{$value})
+        {
+            push @missing_references, [$what, $row->GetValue($report_key), $value];
+        }
+    }
+
+    return @missing_references;
+}
+
+
+
+
+=head CheckAllReferences ($msi)
+
+    Check references from files and registry entries to components,
+    from components to features, and between features.
+
+=cut
+
+sub CheckAllReferences ($)
+{
+    my ($msi) = @_;
+
+    # Set up tables and maps for easy iteration and fast lookups.
+    
+    my $feature_table = $msi->GetTable("Feature");
+    my $component_table = $msi->GetTable("Component");
+    my $feature_component_table = $msi->GetTable("FeatureComponents");
+    my $file_table = $msi->GetTable("File");
+    my $registry_table = $msi->GetTable("Registry");
+    my $directory_table = $msi->GetTable("Directory");
+
+    my %feature_map = map {$_->GetValue("Feature") => $_} @{$feature_table->GetAllRows()};
+    my %component_map = map {$_->GetValue("Component") => $_} @{$component_table->GetAllRows()};
+    my %directory_map = map {$_->GetValue("Directory") => $_} @{$directory_table->GetAllRows()};
+
+    my @missing_references = ();
+    
+    # Check references from files and registry entries to components.
+    push @missing_references, GetMissingReferences(
+        $file_table,
+        "Component_",
+        \%component_map,
+        "file->component",
+        "File");
+    push @missing_references, GetMissingReferences(
+        $registry_table,
+        "Component_",
+        \%component_map,
+        "registry->component",
+        "Registry");
+
+    # Check references between features and components.
+    push @missing_references, GetMissingReferences(
+        $feature_component_table,
+        "Feature_",
+        \%feature_map,
+        "component->feature",
+        "Component_");
+    push @missing_references, GetMissingReferences(
+        $feature_component_table,
+        "Component_",
+        \%component_map,
+        "feature->component",
+        "Feature_");
+
+    # Check references between features.
+    push @missing_references, GetMissingReferences(
+        $feature_table,
+        'Feature_Parent',
+        \%feature_map,
+        "feature->feature",
+        'Feature');
+
+    # Check references between directories.
+    push @missing_references, GetMissingReferences(
+        $directory_table,
+        'Directory_Parent',
+        \%directory_map,
+        "directory->directory",
+        'Directory');
+
+    # Check references from components to directories.
+    push @missing_references, GetMissingReferences(
+        $component_table,
+        'Directory_',
+        \%directory_map,
+        "component->directory",
+        'Component');
+
+    # Check references from components to files (via the .
+
+    # Report the result.
+    if (scalar @missing_references > 0)
+    {
+        $installer::logger::Info->printf("Error: there are %d missing references\n", scalar @missing_references);
+        foreach my $reference (@missing_references)
+        {
+            $installer::logger::Info->printf("    %s : %s -> %s\n",
+                $reference->[0],
+                $reference->[1],
+                $reference->[2]);
+        }
+        return 0;
+    }
+    else
+    {
+        $installer::logger::Info->printf("OK: all references are OK\n");
+        return 1;
+
+    }
+}
+
+
+
+
 sub Check ($$$$)
 {
     my ($source_msi, $target_msi, $variables, $product_name) = @_;
@@ -971,22 +1286,37 @@ sub Check ($$$$)
 
     my $result = 1;
 
-    $result &&= CheckUpgradeCode($source_msi, $target_msi);
-    $result &&= CheckProductCode($source_msi, $target_msi);
-    $result &&= CheckBuildIdCode($source_msi, $target_msi);
-    $result &&= CheckProductName($source_msi, $target_msi);
-    $result &&= CheckRemovedFiles($source_msi, $target_msi);
-    $result &&= CheckNewFiles($source_msi, $target_msi);
-    $result &&= CheckComponentSets($source_msi, $target_msi);
-    $result &&= CheckComponentValues($source_msi, $target_msi, $variables);
-    $result &&= CheckFileSequence($source_msi, $target_msi);
-    $result &&= CheckFileSequenceUnique($source_msi, $target_msi);
-    $result &&= CheckFileSequenceHoles($source_msi, $target_msi);
-    $result &&= CheckRegistryItems($source_msi, $target_msi, $product_name);
-    $result &&= CheckComponentKeyPath($source_msi, $target_msi);
+    # Using &= below to avoid lazy evaluation.  Even if there are errors, all checks shall be run.
+    $result &= CheckUpgradeCode($source_msi, $target_msi);
+    $result &= CheckProductCode($source_msi, $target_msi);
+    $result &= CheckBuildIdCode($source_msi, $target_msi);
+    $result &= CheckProductName($source_msi, $target_msi);
+    $result &= CheckRemovedFiles($source_msi, $target_msi);
+    $result &= CheckNewFiles($source_msi, $target_msi);
+    $result &= CheckFeatureSets($source_msi, $target_msi);
+    $result &= CheckRemovedComponents($source_msi, $target_msi);
+    $result &= CheckAddedComponents($source_msi, $target_msi);
+    $result &= CheckComponentValues($source_msi, $target_msi, $variables);
+    $result &= CheckFileSequence($source_msi, $target_msi);
+    $result &= CheckFileSequenceUnique($source_msi, $target_msi);
+    $result &= CheckFileSequenceHoles($source_msi, $target_msi);
+    $result &= CheckRegistryItems($source_msi, $target_msi, $product_name);
+    $result &= CheckComponentKeyPath($source_msi, $target_msi);
+    $result &= CheckAllReferences($target_msi);
 
     $installer::logger::Info->decrease_indentation();
 
+    if ($result)
+    {
+        $installer::logger::Info->printf("OK: Source and target releases are compatible.\n");
+    }
+    else
+    {
+        $installer::logger::Info->printf("Error: Source and target releases are not compatible.\n");
+        $installer::logger::Info->printf("       => Can not create patch.\n");
+        $installer::logger::Info->printf("       Did you create the target installation set with 'release=t' ?\n");
+    }
+
     return $result;
 }
 
@@ -1232,8 +1562,8 @@ sub CreatePcp ($$$$$$%)
     }
     my $pcp = installer::patch::Msi->new(
         $pcp_filename,
-        undef,
-        undef,
+        $target_msi->{'version'},
+        $target_msi->{'is_current_version'},
         $language,
         $context->{'product-name'});
 
@@ -1281,7 +1611,6 @@ sub ShowLog ($$$$)
             "/o", "'".installer::patch::Tools::ToWindowsPath($destination_path)."'");
         printf("running command $command\n");
         my $response = qx($command);
-        printf("response is '%s'\n", $response);
         my @candidates = glob($destination_path . "/Details*");
         foreach my $candidate (@candidates)
         {
@@ -1306,8 +1635,8 @@ sub ShowLog ($$$$)
             close $in;
             close $out;
             
-            my $URL = $new_name;
-            $URL =~ s/\/c\//c|\//;
+            my $URL = File::Spec->rel2abs($new_name);
+            $URL =~ s/\/cygdrive\/(.)\//$1|\//;
             $URL =~ s/^(.):/$1|/;
             $URL = "file:///". $URL;
             $installer::logger::Info->printf("open %s in your browser to see the log messages\n", $URL);
@@ -1344,13 +1673,17 @@ sub CreateMsp ($)
             || die ("can not create temporary path ".$temporary_msimsp_path);
     }
     $installer::logger::Info->printf("running msimsp.exe, that will take a while\n");
+    my $create_performance_log = 0;
     my $command = join(" ",
         "msimsp.exe",
         "-s", "'".installer::patch::Tools::ToWindowsPath($pcp->{'filename'})."'",
         "-p", "'".installer::patch::Tools::ToWindowsPath($pcp->{'msp_filename'})."'",
         "-l", "'".installer::patch::Tools::ToWindowsPath($log_filename)."'",
         "-f", "'".installer::patch::Tools::ToWindowsPath($temporary_msimsp_path)."'");
-#	    "-lp", MsiTools::ToEscapedWindowsPath($performance_log_filename),
+    if ($create_performance_log)
+    {
+        $command .= " -lp " . MsiTools::ToEscapedWindowsPath($performance_log_filename);
+    }
     $installer::logger::Info->printf("running command %s\n", $command);
     my $response = qx($command);
     $installer::logger::Info->printf("response of msimsp is %s\n", $response);
@@ -1361,7 +1694,60 @@ sub CreateMsp ($)
 
     # Show the log file that was created by the msimsp.exe command.
     ShowLog($log_path, $log_filename, $log_basename, "msp creation");
-    ShowLog($log_path, $performance_log_filename, $performance_log_basename, "msp creation perf");
+    if ($create_performance_log)
+    {
+        ShowLog($log_path, $performance_log_filename, $performance_log_basename, "msp creation perf");
+    }
+}
+
+
+sub ProvideMsis ($$$)
+{
+    my ($context, $variables, $language) = @_;
+
+    # 2a. Provide .msi and .cab files and unpack .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(
+	       $context->{'source-version'},
+	       0,
+	       $language,
+	       "msi",
+	       $context->{'product-name'}))
+    {
+        die "could not provide unpacked .cab file";
+    }
+    my $source_msi = installer::patch::Msi->FindAndCreate(
+        $context->{'source-version'},
+        0,
+        $language,
+        $context->{'product-name'});
+    die unless defined $source_msi;
+    die unless $source_msi->IsValid();
+    $installer::logger::Info->decrease_indentation();
+
+    # 2b. Provide .msi and .cab files and unpacked .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(
+               $context->{'target-version'},
+               1,
+               $language,
+               "msi",
+               $context->{'product-name'}))
+    {
+        die;
+    }
+    my $target_msi = installer::patch::Msi->FindAndCreate(
+        $context->{'target-version'},
+        0,
+        $language,
+        $context->{'product-name'});
+    die unless defined $target_msi;
+    die unless $target_msi->IsValid();
+    $installer::logger::Info->decrease_indentation();
+
+    return ($source_msi, $target_msi);
 }
 
 
@@ -1400,57 +1786,23 @@ sub CreatePatch ($$)
         ->{$context->{'package-format'}};
 
     # 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",
-        join("', '", @requested_languages),
-        join("', '", @valid_languages));
-    foreach my $language (@valid_languages)
+    my $language = $context->{'language'};
+    my %no_ms_lang_locale_map = map {$_=>1} @installer::globals::noMSLocaleLangs;
+    if (defined $no_ms_lang_locale_map{$language})
     {
-        $installer::logger::Info->printf("processing language '%s'\n", $language);
-        $installer::logger::Info->increase_indentation();
+        $language = "en-US_".$language;
+    }
 
-        # 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'});
+    if ( ! IsLanguageValid($context, $release_data, $language))
+    {
+        $installer::logger::Info->printf("can not create patch for language '%s'\n", $language);
+    }
+    else
+    {
+        $installer::logger::Info->printf("processing language '%s'\n", $language);
         $installer::logger::Info->increase_indentation();
-        if ( ! installer::patch::InstallationSet::ProvideUnpackedCab(
-            $context->{'source-version'},
-            0,
-            $language,
-            "msi",
-            $context->{'product-name'}))
-        {
-            die "could not provide unpacked .cab file";
-        }
-        my $source_msi = installer::patch::Msi->FindAndCreate(
-            $context->{'source-version'},
-            0,
-            $language,
-            $context->{'product-name'});
-        die unless $source_msi->IsValid();
 
-        $installer::logger::Info->decrease_indentation();
-
-        # 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(
-            $context->{'target-version'},
-            1,
-            $language,
-            "msi",
-            $context->{'product-name'}))
-        {
-            die;
-        }
-        my $target_msi = installer::patch::Msi->FindAndCreate(
-            $context->{'target-version'},
-            0,
-            $language,
-            $context->{'product-name'});
-        die unless defined $target_msi;
-        die unless $target_msi->IsValid();
-        $installer::logger::Info->decrease_indentation();
+        my ($source_msi, $target_msi) = ProvideMsis($context, $variables, $language);
 
         # Trigger reading of tables.
         foreach my $table_name (("File", "Component", "Registry"))
@@ -1463,15 +1815,8 @@ sub CreatePatch ($$)
         # 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");
-            $installer::logger::Info->printf("       => Can not create patch.\n");
-            $installer::logger::Info->printf("       Did you create the target installation set with 'release=t' ?\n");
             exit(1);
         }
-        else
-        {
-            $installer::logger::Info->printf("OK: Source and target releases are compatible.\n");
-        }
 
         # Provide the base path for creating .pcp and .mcp file.
         my $msp_path = File::Spec->catfile(
@@ -1479,12 +1824,12 @@ sub CreatePatch ($$)
             $context->{'product-name'},
             "msp",
             sprintf("%s_%s",
-              installer::patch::Version::ArrayToDirectoryName(
-                installer::patch::Version::StringToNumberArray(
-                    $source_msi->{'version'})),
-              installer::patch::Version::ArrayToDirectoryName(
-                installer::patch::Version::StringToNumberArray(
-                    $target_msi->{'version'}))),
+                installer::patch::Version::ArrayToDirectoryName(
+                    installer::patch::Version::StringToNumberArray(
+                        $source_msi->{'version'})),
+                installer::patch::Version::ArrayToDirectoryName(
+                    installer::patch::Version::StringToNumberArray(
+                        $target_msi->{'version'}))),
             $language
             );
         File::Path::make_path($msp_path) unless -d $msp_path;
@@ -1508,6 +1853,58 @@ sub CreatePatch ($$)
 
 
 
+
+sub CheckPatchCompatability ($$)
+{
+    my ($context, $variables) = @_;
+    
+    $installer::logger::Info->printf("patch will update product %s from %s to %s\n",
+        $context->{'product-name'},
+        $context->{'source-version'},
+        $context->{'target-version'});
+
+    my $release_data = installer::patch::ReleasesList::Instance()
+        ->{$context->{'source-version'}}
+        ->{$context->{'package-format'}};
+
+    # 1. Determine the set of languages for which we can create patches.
+    my $language = $context->{'language'};
+    my %no_ms_lang_locale_map = map {$_=>1} @installer::globals::noMSLocaleLangs;
+    if (defined $no_ms_lang_locale_map{$language})
+    {
+        $language = "en-US_".$language;
+    }
+
+    if ( ! IsLanguageValid($context, $release_data, $language))
+    {
+        $installer::logger::Info->printf("can not create patch for language '%s'\n", $language);
+    }
+    else
+    {
+        $installer::logger::Info->printf("processing language '%s'\n", $language);
+        $installer::logger::Info->increase_indentation();
+
+        my ($source_msi, $target_msi) = ProvideMsis($context, $variables, $language);
+
+        # Trigger reading of tables.
+        foreach my $table_name (("File", "Component", "Registry"))
+        {
+            $source_msi->GetTable($table_name);
+            $target_msi->GetTable($table_name);
+            $installer::logger::Info->printf("read %s table (source and target\n", $table_name);
+        }
+        
+        # 3. Check if the source and target msis fullfil all necessary requirements.
+        if ( ! Check($source_msi, $target_msi, $variables, $context->{'product-name'}))
+        {
+            exit(1);
+        }
+    }
+}
+
+
+
+
 =cut ApplyPatch ($context, $variables)
 
     This is for testing only.
@@ -1522,7 +1919,6 @@ sub ApplyPatch ($$)
         $context->{'product-name'},
         $context->{'source-version'},
         $context->{'target-version'});
-    my @languages = GetLanguages();
 
     my $source_version_dirname = installer::patch::Version::ArrayToDirectoryName(
       installer::patch::Version::StringToNumberArray(
@@ -1531,41 +1927,45 @@ sub ApplyPatch ($$)
       installer::patch::Version::StringToNumberArray(
           $context->{'target-version'}));
 
-    foreach my $language (@languages)
+    my $language = $context->{'language'};
+    my %no_ms_lang_locale_map = map {$_=>1} @installer::globals::noMSLocaleLangs;
+    if (defined $no_ms_lang_locale_map{$language})
     {
-        my $msp_filename = File::Spec->catfile(
-            $context->{'output-path'},
-            $context->{'product-name'},
-            "msp",
-            $source_version_dirname . "_" . $target_version_dirname,
-            $language,
-            "openoffice.msp");
-        if ( ! -f $msp_filename)
-        {
-            $installer::logger::Info->printf("%s does not point to a valid file\n", $msp_filename);
-            next;
-        }
-        
-        my $log_path = File::Spec->catfile(dirname($msp_filename), "log");
-        my $log_basename = "apply-msp";
-        my $log_filename = File::Spec->catfile($log_path, $log_basename.".log");
+        $language = "en-US_".$language;
+    }
+
+    my $msp_filename = File::Spec->catfile(
+        $context->{'output-path'},
+        $context->{'product-name'},
+        "msp",
+        $source_version_dirname . "_" . $target_version_dirname,
+        $language,
+        "openoffice.msp");
+    if ( ! -f $msp_filename)
+    {
+        $installer::logger::Info->printf("%s does not point to a valid file\n", $msp_filename);
+        next;
+    }
         
-        my $command = join(" ",
-            "msiexec.exe",
-            "/update", "'".installer::patch::Tools::ToWindowsPath($msp_filename)."'",
-            "/L*xv!", "'".installer::patch::Tools::ToWindowsPath($log_filename)."'",
-            "REINSTALL=ALL",
+    my $log_path = File::Spec->catfile(dirname($msp_filename), "log");
+    my $log_basename = "apply-msp";
+    my $log_filename = File::Spec->catfile($log_path, $log_basename.".log");
+    
+    my $command = join(" ",
+        "msiexec.exe",
+        "/update", "'".installer::patch::Tools::ToWindowsPath($msp_filename)."'",
+        "/L*xv!", "'".installer::patch::Tools::ToWindowsPath($log_filename)."'",
+        "REINSTALL=ALL",
 #            "REINSTALLMODE=vomus",
-            "REINSTALLMODE=omus",
-            "MSIENFORCEUPGRADECOMPONENTRULES=1");
+        "REINSTALLMODE=omus",
+        "MSIENFORCEUPGRADECOMPONENTRULES=1");
     
-        printf("executing command %s\n", $command);
-        my $response = qx($command);
-        Encode::from_to($response, "UTF16LE", "UTF8");
-        printf("response was '%s'\n", $response);
+    printf("executing command %s\n", $command);
+    my $response = qx($command);
+    Encode::from_to($response, "UTF16LE", "UTF8");
+    printf("response was '%s'\n", $response);
     
-        ShowLog($log_path, $log_filename, $log_basename, "msp application");
-    }
+    ShowLog($log_path, $log_filename, $log_basename, "msp application");
 }
 
 
@@ -1802,12 +2202,17 @@ sub UpdateReleasesXML($$)
 
 sub main ()
 {
-    installer::logger::SetupSimpleLogging(undef);
     my $context = ProcessCommandline();
+    installer::logger::starttime();
+    $installer::logger::Global->add_timestamp("starting logging");
+#    installer::logger::SetupSimpleLogging(undef);
+    
     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'};
+    die sprintf("ERROR: package format %s is not supported", $context->{'package-format'})
+        unless defined $context->{'package-format'} ne "msi";
 
     my ($variables, undef, undef) = installer::ziplist::read_openoffice_lst_file(
         $context->{'lst-file'},
@@ -1815,6 +2220,22 @@ sub main ()
         undef);
     DetermineVersions($context, $variables);
 
+    if ($context->{'command'} =~ /create|check/)
+    {
+        $installer::logger::Lang->set_filename(
+            File::Spec->catfile(
+                $context->{'output-path'},
+                $context->{'product-name'},
+                "msp",
+                $context->{'source-version-dash'} . "_" . $context->{'target-version-dash'},
+                $context->{'language'},
+                "log",
+                "patch-creation.log"));
+        $installer::logger::Lang->copy_lines_from($installer::logger::Global);
+        $installer::logger::Lang->set_forward(undef);
+        $installer::logger::Info->set_forward($installer::logger::Lang);
+    }
+
     if ($context->{'command'} eq "create")
     {
         CreatePatch($context, $variables);
@@ -1827,6 +2248,10 @@ sub main ()
     {
         UpdateReleasesXML($context, $variables);
     }
+    elsif ($context->{'command'} eq "check")
+    {
+        CheckPatchCompatability($context, $variables);
+    }
 }
 
 

Modified: openoffice/trunk/main/solenv/bin/release_prepare.pl
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/release_prepare.pl?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/release_prepare.pl (original)
+++ openoffice/trunk/main/solenv/bin/release_prepare.pl Fri Dec 13 12:05:15 2013
@@ -32,7 +32,7 @@ use Getopt::Long;
 use Pod::Usage;
 use Digest;
 
-use Carp::Always;
+#use Carp::Always;
 
 use strict;