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 [1/2] - in /openoffice/trunk/main: instsetoo_native/util/ solenv/bin/ solenv/bin/modules/installer/ solenv/bin/modules/installer/patch/ solenv/bin/modules/installer/windows/

Author: af
Date: Fri Dec 13 12:05:15 2013
New Revision: 1550699

URL: http://svn.apache.org/r1550699
Log:
123531: Fixes for the patch creation.

Modified:
    openoffice/trunk/main/instsetoo_native/util/makefile.mk
    openoffice/trunk/main/solenv/bin/make_installer.pl
    openoffice/trunk/main/solenv/bin/modules/installer/globals.pm
    openoffice/trunk/main/solenv/bin/modules/installer/languages.pm
    openoffice/trunk/main/solenv/bin/modules/installer/patch/InstallationSet.pm
    openoffice/trunk/main/solenv/bin/modules/installer/patch/Msi.pm
    openoffice/trunk/main/solenv/bin/modules/installer/patch/Tools.pm
    openoffice/trunk/main/solenv/bin/modules/installer/windows/component.pm
    openoffice/trunk/main/solenv/bin/modules/installer/windows/directory.pm
    openoffice/trunk/main/solenv/bin/modules/installer/windows/feature.pm
    openoffice/trunk/main/solenv/bin/modules/installer/windows/file.pm
    openoffice/trunk/main/solenv/bin/modules/installer/windows/msiglobal.pm
    openoffice/trunk/main/solenv/bin/modules/installer/windows/property.pm
    openoffice/trunk/main/solenv/bin/modules/installer/windows/registry.pm
    openoffice/trunk/main/solenv/bin/patch_tool.pl
    openoffice/trunk/main/solenv/bin/release_prepare.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=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/instsetoo_native/util/makefile.mk (original)
+++ openoffice/trunk/main/instsetoo_native/util/makefile.mk Fri Dec 13 12:05:15 2013
@@ -87,6 +87,7 @@ help .PHONY :
 	@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 "    patch-check            check if patch can be created (part of patch-create)"
 	@echo 
 	@echo "Most targets (all except aoo_srcrelease and updatepack) accept suffixes"
 	@echo "    add _<language> to build a target for one language only"
@@ -162,12 +163,17 @@ updatepack:
 	$(PERL) -w $(SOLARENV)$/bin$/packager.pl
 
 
-# The naming schema of targets is this: target_language.package
-# where 'target' is the target base name (as openoffice or sdkoo)
-#       'language' is the language name (like en-US or fr)
-#       'package' is the package format (like msi or deb)
 
-.IF "$(alllangiso)"!=""
+.IF "$(alllangiso)"==""
+openoffice:
+	@echo no languages specified => aborting packing
+
+.ELSE	# "$(alllangiso)"==""
+
+# The naming schema of targets is this: <target>_<language>.<package>
+# where <target> is the target base name (like openoffice or sdkoo)
+#       <language> is the language name (like en-US or fr)
+#       <package> is the package format (like archive, msi, deb, rpm, dmg)
 
 # Add dependencies of basic targets on language specific targets.
 openoffice: $(foreach,i,$(alllangiso) openoffice_$i)
@@ -259,15 +265,6 @@ $(foreach,P,$(PACKAGE_FORMATS) $(foreach
 		$(PRJ)$/util$/update.xml	\
 		> $(MISC)/$(@:b)_$(RTL_OS)_$(RTL_ARCH)$(@:e).update.xml
 
-#$(foreach,L,$(alllangiso) openoffice_$L.archive) :
-#	$(MAKE_INSTALLER_COMMAND) 		\
-#		-p Apache_OpenOffice		\
-#		-msitemplate $(MSIOFFICETEMPLATEDIR)
-#	$(GEN_UPDATE_INFO_COMMAND)		\
-#		--product Apache_OpenOffice	\
-#		$(PRJ)$/util$/update.xml	\
-#		> $(MISC)/$(@:b)_$(RTL_OS)_$(RTL_ARCH)$(@:e).update.xml
-
 #openofficewithjre_%{$(PKGFORMAT:^".")} :
 $(foreach,P,$(PACKAGE_FORMATS) $(foreach,L,$(alllangiso) openofficewithjre_$L.$P)) .PHONY :
 	$(MAKE_INSTALLER_COMMAND) -p Apache_OpenOffice_wJRE -msitemplate $(MSIOFFICETEMPLATEDIR)
@@ -301,11 +298,7 @@ $(foreach,P,$(PACKAGE_FORMATS) $(foreach
 $(foreach,P,$(PACKAGE_FORMATS) $(foreach,L,$(alllangiso) sdkoodev_$L.$P)) .PHONY :
 	$(MAKE_INSTALLER_COMMAND) -p Apache_OpenOffice_Dev_SDK -msitemplate $(MSISDKOOTEMPLATEDIR) -dontstrip
 
-.ELSE			# "$(alllangiso)"!=""
-openoffice:
-	@echo cannot pack nothing...
-
-.ENDIF			# "$(alllangiso)"!=""
+.ENDIF	# "$(alllangiso)"==""
 
 $(BIN)$/%.py : $(SOLARSHAREDBIN)$/pyuno$/%.py
 	$(COPY) $< $@
@@ -344,6 +337,16 @@ patch-update-releases-xml .PHONY:
 		--output-path $(OUT)						\
 		--lst-file $(PRJ)$/util$/openoffice.lst\
 		--target-version 4.0.1
+$(foreach,P,$(PACKAGE_FORMATS) $(foreach,L,$(alllangiso) patch-check_$L.$P)) .PHONY :
+	@echo building $@
+	perl -I $(SOLARENV)$/bin/modules $(SOLARENV)$/bin$/patch_tool.pl	\
+		check								\
+		--product-name Apache_OpenOffice				\
+		--output-path $(OUT)						\
+		--data-path $(PRJ)$/data					\
+		--lst-file $(PRJ)$/util$/openoffice.lst				\
+		--language $(subst,$(@:s/_/ /:1)_, $(@:b))			\
+		--package-format $(@:e:s/.//)
 
 $(PRJ)$/data :
 	mkdir $@

Modified: openoffice/trunk/main/solenv/bin/make_installer.pl
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/make_installer.pl?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/make_installer.pl (original)
+++ openoffice/trunk/main/solenv/bin/make_installer.pl Fri Dec 13 12:05:15 2013
@@ -303,11 +303,18 @@ sub MakeWindowsBuild ($$$$$$$$$$$$$$$$$$
     $modulesinproductlanguageresolvedarrayref = installer::windows::feature::sort_feature(
         $modulesinproductlanguageresolvedarrayref);
     
-    installer::windows::feature::create_feature_table(
-        $modulesinproductlanguageresolvedarrayref,
-        $newidtdir,
-        $languagesarrayref,
-        $allvariableshashref);
+	foreach my $onelanguage (@$languagesarrayref)
+	{
+        my $features = installer::windows::feature::prepare_feature_table(
+            $modulesinproductlanguageresolvedarrayref,
+            $onelanguage,
+            $allvariableshashref);
+        $features = installer::windows::feature::add_missing_features($features);
+        installer::windows::feature::create_feature_table(
+            $newidtdir,
+            $onelanguage,
+            $features);
+    }
 
     installer::windows::featurecomponent::create_featurecomponent_table(
         $filesinproductlanguageresolvedarrayref,

Modified: openoffice/trunk/main/solenv/bin/modules/installer/globals.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/globals.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/globals.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/globals.pm Fri Dec 13 12:05:15 2013
@@ -441,7 +441,7 @@ BEGIN
 	@environmentvariables = ( "SOLARVERSION", "GUI", "WORK_STAMP", "OUTPATH", "LOCAL_OUT", "LOCAL_COMMON_OUT" );
 	@packagelistitems = ("module", "solarispackagename", "packagename", "copyright", "vendor", "description" );
 	@languagepackfeature =();
-	@featurecollector =();
+	%featurecollector =();
 	$msiassemblyfiles = "";
 	$nsisfilename = "Nsis";
 	$macinstallfilename = "macinstall.ulf";

Modified: openoffice/trunk/main/solenv/bin/modules/installer/languages.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/languages.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/languages.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/languages.pm Fri Dec 13 12:05:15 2013
@@ -479,13 +479,20 @@ sub get_normalized_language ($)
 
     if (ref($language) eq "ARRAY")
     {
-        if (scalar @$language > 1 && $language->[0] eq "en-US")
+        if (scalar @$language > 1)
         {
-            return $language->[1];
+            if ($language->[0] eq "en-US")
+            {
+                return $language->[1];
+            }
+            else
+            {
+                return $language->[0];
+            }
         }
         else
         {
-            return $language;
+            return join("_", @$language);
         }
     }
     elsif ($language =~ /^.*?_(.*)$/)

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=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/patch/InstallationSet.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/patch/InstallationSet.pm Fri Dec 13 12:05:15 2013
@@ -27,34 +27,85 @@ use installer::logger;
 
 use strict;
 
-# TODO: Detect the location of 7z.exe
-my $Unpacker = "/c/Program\\ Files/7-Zip/7z.exe";
+# Call Get7Zip() to get access to the filename of the 7z executable.
+my $SevenZip = undef;
+
+
+=head1 NAME
+
+    package installer::patch::InstallationSet  -  Functions for handling installation sets
+
+=head1 DESCRIPTION
+
+    This package contains functions for unpacking the .exe files that
+    are created by the NSIS installer creator and the .cab files in
+    the installation sets.
+
+=cut
 
 
 
-# TODO: Is there a touch in a standard library?
-sub touch ($)
+
+=head2 Detect7ZipOnWindows ()
+
+    7Zip seems to be the only program able to unpack an NSIS installer.
+    Search for it.
+
+=cut
+
+sub Detect7ZipOnWindows ()
 {
-    my ($filename) = @_;
+    # Use 'reg query' to read registry entry from Windows registry.
+    my $registry_key = "HKEY_CURRENT_USER\\\\Software\\\\7-Zip";
+    my $registry_value_name = "Path";
+    my $command = sprintf("reg query %s /v %s", $registry_key, $registry_value_name);
+    my $response = qx($command);
 
-    open my $out, ">", $filename;
-    close $out;
+    # Process the response.
+    my $path_to_7zip = undef;
+    if ($response =~ /\s+REG_SZ\s+([^\r\n]*)/m)
+    {
+        $path_to_7zip = $1;
+    }
+
+    # If that failed, then make an educated guess.
+    if ( ! defined $path_to_7zip)
+    {
+        $path_to_7zip = "c:\\Program Files\\7-Zip\\";
+    }
+
+    # Check if the executable exists and is, well, executable.
+    return undef unless -d $path_to_7zip;
+    my $fullname = File::Spec->catfile($path_to_7zip, "7z.exe");
+    return undef unless -f $fullname;
+    return undef unless -x $fullname;
+
+    return $fullname;
 }
 
 
 
 
-=head1 NAME
+sub Get7Zip ()
+{
+    if ( ! defined $SevenZip)
+    {
+        if ($ENV{'OS'} eq "WNT")
+        {
+            $SevenZip = Detect7ZipOnWindows();
+        }
+        if ( ! defined $SevenZip)
+        {
+            # Use an empty string to avoid repeated (and failing) detections of a missing 7z.
+            $SevenZip = "";
+        }
+    }
 
-    package installer::patch::InstallationSet  -  Functions for handling installation sets
+    return $SevenZip eq "" ? undef : $SevenZip;
+}
 
-=head1 DESCRIPTION
 
-    This package contains functions for unpacking the .exe files that
-    are created by the NSIS installer creator and the .cab files in
-    the installation sets.
 
-=cut
 
 sub UnpackExe ($$)
 {
@@ -69,12 +120,18 @@ sub UnpackExe ($$)
     my $windows_filename = installer::patch::Tools::ToEscapedWindowsPath($filename);
     my $windows_destination_path = installer::patch::Tools::ToEscapedWindowsPath($destination_path);
     my $command = join(" ",
-        $Unpacker,
+        "\"".Get7Zip()."\"",
         "x",
         "-y",
         "-o".$windows_destination_path,
         $windows_filename);
     my $result = qx($command);
+    if ( ! $result)
+    {
+        installer::exiter::exit_program(
+            "ERROR: can not unpack downloadable installation set: ".$!,
+            "installer::patch::InstallationSet::UnpackExe");
+    }
 
     # Check the existence of the .cab files.
     my $cab_filename = File::Spec->catfile($destination_path, "openoffice1.cab");
@@ -119,33 +176,42 @@ sub UnpackCab ($$$)
     if ( -d $temporary_destination_path)
     {
         # Temporary directory already exists => cab file has already been unpacked (flat), nothing to do.
+        printf("%s exists\n", $temporary_destination_path);
         $installer::logger::Info->printf("cab file has already been unpacked to flat structure\n");
     }
     else
     {
         UnpackCabFlat($cab_filename, $temporary_destination_path, $file_table);
     }
-    
+
     # Step 3
     # Move the files to their destinations.
     File::Path::make_path($destination_path);
     $installer::logger::Info->printf("moving files to their directories\n");
+    my $directory_map = $msi->GetDirectoryMap();
+    my $office_menu_folder_name = $directory_map->{'INSTALLLOCATION'}->{'target_long_name'};
     my $count = 0;
     foreach my $file_row (@{$file_table->GetAllRows()})
     {
         my $unique_name = $file_row->GetValue('File');
-        my $directory_item = $file_map->{$unique_name}->{'directory'};
-        my $source_full_name = $directory_item->{'full_source_long_name'};
-
+        my $file_item = $file_map->{$unique_name};
+        my $directory_item = $file_item->{'directory'};
+        my $long_file_name = $file_item->{'long_name'};
+        my $full_name = $directory_item->{'full_source_long_name'};
+        # Strip away the leading OfficeMenuFolder part.
+        $full_name =~ s/^$office_menu_folder_name\///;
         my $flat_filename = File::Spec->catfile($temporary_destination_path, $unique_name); 
-        my $dir_path = File::Spec->catfile($destination_path, $source_full_name);
-        my $dir_filename = File::Spec->catfile($dir_path, $unique_name);
+        my $dir_path = File::Spec->catfile($destination_path, $full_name);
+        my $dir_filename = File::Spec->catfile($dir_path, $long_file_name);
 
         if ( ! -d $dir_path)
         {
             File::Path::make_path($dir_path);
         }
-        File::Copy::move($flat_filename, $dir_filename);
+
+        $installer::logger::Lang->printf("moving %s to %s\n", $flat_filename, $dir_filename);
+        File::Copy::move($flat_filename, $dir_filename)
+            || die("can not move file ".$flat_filename.":".$!);
 
         ++$count;
     }
@@ -180,7 +246,7 @@ sub UnpackCabFlat ($$$)
     my $windows_cab_filename = installer::patch::Tools::ToEscapedWindowsPath($cab_filename);
     my $windows_destination_path = installer::patch::Tools::ToEscapedWindowsPath($destination_path);
     my $command = join(" ",
-        $Unpacker,
+        "\"".Get7Zip()."\"",
         "x", "-o".$windows_destination_path,
         $windows_cab_filename,
         "-y");
@@ -449,7 +515,8 @@ sub ProvideDownloadSet ($$$)
     my ($version, $language, $package_format) = @_;
 
     my $release_item = installer::patch::ReleasesList::Instance()->{$version}->{$package_format}->{$language};
-    
+    return undef unless defined $release_item;
+
     # Get basename of installation set from URL.
     $release_item->{'URL'} =~ /^(.*)\/([^\/]+)$/;
     my ($location, $basename) = ($1,$2);
@@ -558,7 +625,7 @@ sub ProvideUnpackedExe ($$$$$)
 	    $file_count,
 	    $directory_count);
 
-        touch($unpacked_exe_flag_filename);
+        installer::patch::Tools::touch($unpacked_exe_flag_filename);
 
         return 1;
     }
@@ -580,7 +647,7 @@ sub ProvideUnpackedExe ($$$$$)
                 $installer::logger::Info->printf("downloadable installation set has been unpacked to\n");
                 $installer::logger::Info->printf("    %s\n", $unpacked_exe_path);
 
-                touch($unpacked_exe_flag_filename);
+                installer::patch::Tools::touch($unpacked_exe_flag_filename);
 
                 return 1;
             }
@@ -774,7 +841,7 @@ sub ProvideUnpackedCab ($$$$$)
             $installer::logger::Info->printf("unpacked cab file '%s'\n", $cab_filename);
             $installer::logger::Info->printf("    to '%s'\n", $unpacked_cab_path);
 
-            touch($unpacked_cab_flag_filename);
+            installer::patch::Tools::touch($unpacked_cab_flag_filename);
             
             return 1;
         }

Modified: openoffice/trunk/main/solenv/bin/modules/installer/patch/Msi.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/patch/Msi.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/patch/Msi.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/patch/Msi.pm Fri Dec 13 12:05:15 2013
@@ -76,7 +76,7 @@ sub FindAndCreate($$$$$)
 
 =cut
 
-sub new ($$$$$$)
+sub new ($$;$$$$)
 {
     my ($class, $filename, $version, $is_current_version, $language, $product_name) = @_;
 
@@ -102,6 +102,22 @@ sub new ($$$$$$)
     };
     bless($self, $class);
 
+    # Fill in some missing values from the 'Properties' table.
+    if ( ! (defined $version && defined $language && defined $product_name))
+    {
+        my $property_table = $self->GetTable("Property");
+
+        $self->{'version'} = $property_table->GetValue("Property", "DEFINEDVERSION", "Value")
+            unless defined $self->{'version'};
+        $self->{'product_name'} = $property_table->GetValue("Property", "DEFINEDPRODUCT", "Value")
+            unless defined $self->{'product_name'};
+        
+        my $language = $property_table->GetValue("Property", "ProductLanguage", "Value");
+        # TODO: Convert numerical language id to language name.
+        $self->{'language'} = $language
+            unless defined $self->{'language'};
+    }
+
     return $self;
 }
 
@@ -181,7 +197,6 @@ sub GetTable ($$)
                 "-f", installer::patch::Tools::ToEscapedWindowsPath($self->{'tmpdir'}),
                 "-e", $table_name);
             my $result = qx($command);
-            print $result;
         }
 
         # Read table into memory.
@@ -323,6 +338,48 @@ sub SplitTargetSourceLongShortName ($)
 }
 
 
+
+
+sub SetupFullNames ($$);
+sub SetupFullNames ($$)
+{
+    my ($item, $directory_map) = @_;
+
+    # Don't process any item twice.
+    return if defined $item->{'full_source_name'};
+
+    my $parent = $item->{'parent'};
+    if (defined $parent)
+    {
+        # Process the parent first.
+        if ( ! defined $parent->{'full_source_long_name'})
+        {
+            SetupFullNames($parent, $directory_map);
+        }
+
+        # Prepend the full names of the parent to our names.
+        $item->{'full_source_long_name'}
+            = $parent->{'full_source_long_name'} . "/" . $item->{'source_long_name'};
+        $item->{'full_source_short_name'}
+            = $parent->{'full_source_short_name'} . "/" . $item->{'source_short_name'};
+        $item->{'full_target_long_name'}
+            = $parent->{'full_target_long_name'} . "/" . $item->{'target_long_name'};
+        $item->{'full_target_short_name'}
+            = $parent->{'full_target_short_name'} . "/" . $item->{'target_short_name'};
+    }
+    else
+    {
+        # Directory has no parent => full names are the same as the name.
+        $item->{'full_source_long_name'} = $item->{'source_long_name'};
+        $item->{'full_source_short_name'} = $item->{'source_short_name'};
+        $item->{'full_target_long_name'} = $item->{'target_long_name'};
+        $item->{'full_target_short_name'} = $item->{'target_short_name'};
+    }
+}
+
+
+
+
 =head2 GetDirectoryMap($self)
 
     Return a map that maps directory unique names (column 'Directory' in table 'Directory')
@@ -339,17 +396,18 @@ sub GetDirectoryMap ($)
         return $self->{'DirectoryMap'};
     }
 
+    # Initialize the directory map.
     my $directory_table = $self->GetTable("Directory");
-    my %dir_map = ();
+    my $directory_map = ();
     foreach my $row (@{$directory_table->GetAllRows()})
     {
         my ($target_long_name, $target_short_name, $source_long_name, $source_short_name)
             = installer::patch::Msi::SplitTargetSourceLongShortName($row->GetValue("DefaultDir"));
         my $unique_name = $row->GetValue("Directory");
-        $dir_map{$unique_name} =
+        $directory_map->{$unique_name} =
         {
             'unique_name' => $unique_name,
-            'parent' => $row->GetValue("Directory_Parent"),
+            'parent_name' => $row->GetValue("Directory_Parent"),
             'default_dir' => $row->GetValue("DefaultDir"),
             'source_long_name' => $source_long_name,
             'source_short_name' => $source_short_name,
@@ -358,49 +416,20 @@ sub GetDirectoryMap ($)
         };
     }
 
-    # Set up full names for all directories.
-    my @todo = map {$_} (keys %dir_map);
-    while (scalar @todo > 0)
+    # Add references to parent directories.
+    foreach my $item (values %$directory_map)
     {
-        my $key = shift @todo;
-        my $item = $dir_map{$key};
-        next if defined $item->{'full_source_name'};
+        $item->{'parent'} = $directory_map->{$item->{'parent_name'}};
+    }
 
-        if ($item->{'parent'} eq "")
-        {
-            # Directory has no parent => full names are the same as the name.
-            $item->{'full_source_long_name'} = $item->{'source_long_name'};
-            $item->{'full_source_short_name'} = $item->{'source_short_name'};
-            $item->{'full_target_long_name'} = $item->{'target_long_name'};
-            $item->{'full_target_short_name'} = $item->{'target_short_name'};
-        }
-        else
-        {
-            my $parent = $dir_map{$item->{'parent'}};
-            if ( defined $parent->{'full_source_long_name'})
-            {
-                # Parent aleady has full names => we can create the full name of the current item.
-                $item->{'full_source_long_name'}
-                    = $parent->{'full_source_long_name'} . "/" . $item->{'source_long_name'};
-                $item->{'full_source_short_name'}
-                    = $parent->{'full_source_short_name'} . "/" . $item->{'source_short_name'};
-                $item->{'full_target_long_name'}
-                    = $parent->{'full_target_long_name'} . "/" . $item->{'target_long_name'};
-                $item->{'full_target_short_name'}
-                    = $parent->{'full_target_short_name'} . "/" . $item->{'target_short_name'};
-            }
-            else
-            {
-                # Parent has to be processed before the current item can be processed.
-                # Push both to the head of the list.
-                unshift @todo, $key;
-                unshift @todo, $item->{'parent'};
-            }
-        }
+    # Set up full names for all directories.
+    foreach my $item (values %$directory_map)
+    {
+        SetupFullNames($item, $directory_map);
     }
 
-    # Postprocess the path names for cleanup.
-    foreach my $item (values %dir_map)
+    # Cleanup the names.
+    foreach my $item (values %$directory_map)
     {
         foreach my $id (
             'full_source_long_name',
@@ -414,7 +443,7 @@ sub GetDirectoryMap ($)
         }
     }
 
-    $self->{'DirectoryMap'} = \%dir_map;
+    $self->{'DirectoryMap'} = $directory_map;
     return $self->{'DirectoryMap'};
 }
 
@@ -455,14 +484,20 @@ sub GetFileMap ($)
     my $file_map = {};
     my $file_component_index = $file_table->GetColumnIndex("Component_");
     my $file_file_index = $file_table->GetColumnIndex("File");
+    my $file_filename_index = $file_table->GetColumnIndex("FileName");
     foreach my $file_row (@{$file_table->GetAllRows()})
     {
         my $component_name = $file_row->GetValue($file_component_index);
         my $directory_name = $component_to_directory_map{$component_name};
         my $unique_name = $file_row->GetValue($file_file_index);
+        my $file_name = $file_row->GetValue($file_filename_index);
+        my ($long_name, $short_name) = SplitLongShortName($file_name);
         $file_map->{$unique_name} = {
             'directory' => $dir_map->{$directory_name},
-            'component_name' => $component_name
+            'component_name' => $component_name,
+            'file_name' => $file_name,
+            'long_name' => $long_name,
+            'short_name' => $short_name
         };
     } 
 

Modified: openoffice/trunk/main/solenv/bin/modules/installer/patch/Tools.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/patch/Tools.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/patch/Tools.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/patch/Tools.pm Fri Dec 13 12:05:15 2013
@@ -59,4 +59,18 @@ sub ToWindowsPath ($)
     return $windows_path;
 }
 
+
+# TODO: Is there a touch in a standard library?
+sub touch ($)
+{
+    my ($filename) = @_;
+
+    open my $out, ">", $filename;
+    close $out;
+}
+
+
+
+
+
 1;

Modified: openoffice/trunk/main/solenv/bin/modules/installer/windows/component.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/windows/component.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/windows/component.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/windows/component.pm Fri Dec 13 12:05:15 2013
@@ -513,7 +513,7 @@ sub get_component_data ($$$$)
         }
         else
         {
-            $guid = installer::windows::msiglobal::create_guid();
+            $guid = "{" . installer::windows::msiglobal::create_guid() . "}";
             $installer::logger::Lang->printf("    creating new guid %s\n", $guid);
         }
         $target_data{$name}->{'component_id'} = $guid;
@@ -521,28 +521,33 @@ sub get_component_data ($$$$)
 
     # Add values for the KeyPath column.
     $installer::logger::Lang->printf("preparing Component->KeyPath values\n");
-    foreach my $name (@$file_component_names,@$registry_component_names)
+    foreach my $component_name (@$file_component_names,@$registry_component_names)
     {
         # Determine the key path.
         my $key_path = $installer::globals::is_release
-            ? $source_data{$name}->{'key_path'}
+            ? $source_data{$component_name}->{'key_path'}
             : undef;
         if (defined $key_path)
         {
-            $installer::logger::Lang->printf("    reusing key path %s\n", $key_path);
+            $installer::logger::Lang->printf("    reusing key path %s for component %s\n",
+                $key_path,
+                $component_name);
         }
         else
         {
-            if ($target_data{$name}->{'is_file'})
+            if ($target_data{$component_name}->{'is_file'})
             {
-                $key_path = get_component_keypath($name, $files);
+                $key_path = get_component_keypath($component_name, $files);
             }
             else
             {
-                $key_path = get_component_keypath($name, $registry_entries); 
+                $key_path = get_component_keypath($component_name, $registry_entries); 
             }
+            $installer::logger::Lang->printf("    created key path %s for component %s\n",
+                $key_path,
+                $component_name);
         }
-        $target_data{$name}->{'key_path'} = $key_path;
+        $target_data{$component_name}->{'key_path'} = $key_path;
     }
 
     return \%target_data;

Modified: openoffice/trunk/main/solenv/bin/modules/installer/windows/directory.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/windows/directory.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/windows/directory.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/windows/directory.pm Fri Dec 13 12:05:15 2013
@@ -750,7 +750,7 @@ sub find_missing_directories ($$)
     {
         my $new_directory_item = {
             'uniquename' => $source_directory_item->{'unique_name'},
-            'uniqueparentname' => $source_directory_item->{'parent'},
+            'uniqueparentname' => $source_directory_item->{'parent_name'},
             'defaultdir' => $source_directory_item->{'default_dir'},
             'HostName' => $source_directory_item->{'full_target_long_name'},
             'componentname' => $source_directory_item->{'component_name'},

Modified: openoffice/trunk/main/solenv/bin/modules/installer/windows/feature.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/windows/feature.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/windows/feature.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/windows/feature.pm Fri Dec 13 12:05:15 2013
@@ -363,78 +363,140 @@ sub add_uniquekey
 # Feature Feature_Parent Title Description Display Level Directory_ Attributes
 #################################################################################
 
-sub create_feature_table
+sub prepare_feature_table ($$$)
 {
-	my ($modulesref, $basedir, $languagesarrayref, $allvariableshashref) = @_;
+	my ($modules, $language, $variables) = @_;
 
-	for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ )
-	{
-		my $onelanguage = ${$languagesarrayref}[$m];
-	
-		my $infoline;
-
-		my @featuretable = ();
-
-		installer::windows::idtglobal::write_idt_header(\@featuretable, "feature");
-
-		for ( my $i = 0; $i <= $#{$modulesref}; $i++ )
-		{
-			my $onefeature = ${$modulesref}[$i];
-
-			# Java and Ada only, if the correct settings are set
-			my $styles = "";
-			if ( $onefeature->{'Styles'} ) { $styles = $onefeature->{'Styles'}; }
-			if (( $styles =~ /\bJAVAMODULE\b/ ) && ( ! ($allvariableshashref->{'JAVAPRODUCT'} ))) { next; }
-			if (( $styles =~ /\bADAMODULE\b/ ) && ( ! ($allvariableshashref->{'ADAPRODUCT'} ))) { next; }
-
-			# Controlling the language!
-			# Only language independent feature or feature with the correct language will be included into the table
-			
-			if (! (!(( $onefeature->{'ismultilingual'} )) || ( $onefeature->{'specificlanguage'} eq $onelanguage )) )  { next; }
-
-			my %feature = ();
-		
-			$feature{'feature'} = get_feature_gid($onefeature);
-			$feature{'feature_parent'} = get_feature_parent($onefeature);
-			# if ( $onefeature->{'ParentID'} eq "" ) { $feature{'feature_parent'} = ""; }	# Root has no parent
-			$feature{'Title'} = $onefeature->{'Name'};
-			$feature{'Description'} = $onefeature->{'Description'};
-			$feature{'Display'} = get_feature_display($onefeature);
-			$feature{'Level'} = get_feature_level($onefeature);
-			$feature{'Directory_'} = get_feature_directory($onefeature);
-			$feature{'Attributes'} = get_feature_attributes($onefeature);
-
-			my $oneline = $feature{'feature'} . "\t" . $feature{'feature_parent'} . "\t" . $feature{'Title'} . "\t"  
-					. $feature{'Description'} . "\t" . $feature{'Display'} . "\t" . $feature{'Level'} . "\t"
-					. $feature{'Directory_'} . "\t" . $feature{'Attributes'} . "\n";
-
-			push(@featuretable, $oneline);
-
-			# collecting all feature in global feature collector (so that properties can be set in property table)
-			if ( ! installer::existence::exists_in_array($feature{'feature'}, \@installer::globals::featurecollector) )
-			{
-				push(@installer::globals::featurecollector, $feature{'feature'});
-			}
-			
-			# collecting all language feature in feature collector for check of language selection
-			if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && ( $onefeature->{'ParentID'} ne $installer::globals::rootmodulegid ))
-			{
-				$installer::globals::multilingual_only_modules{$feature{'feature'}} = 1;
-			}
-
-			# collecting all application feature in global feature collector for check of application selection
-			if ( $styles =~ /\bAPPLICATIONMODULE\b/ )
-			{
-				$installer::globals::application_modules{$feature{'feature'}} = 1;
-			}
-		}
-
-		# Saving the file
-		
-		my $featuretablename = $basedir . $installer::globals::separator . "Feature.idt" . "." . $onelanguage;
-		installer::files::save_file($featuretablename ,\@featuretable);
-        $installer::logger::Lang->printf("Created idt file: %s\n", $featuretablename); 
-	}
+    my $features = [];
+    
+    foreach my $onefeature (@$modules)
+    {
+        # Java and Ada only, if the correct settings are set
+        my $styles = $onefeature->{'Styles'} // "";
+        if (( $styles =~ /\bJAVAMODULE\b/ ) && ( ! ($variables->{'JAVAPRODUCT'} ))) { next; }
+
+        # Controlling the language!
+        # Only language independent feature or feature with the correct language will be included into the table
+        
+        next if $onefeature->{'ismultilingual'} && ($onefeature->{'specificlanguage'} ne $language);
+
+        my $feature_gid =get_feature_gid($onefeature);
+
+        my $feature = {
+            'Feature' => $feature_gid,
+            'Feature_Parent' => get_feature_parent($onefeature),
+            'Title' => $onefeature->{'Name'},
+            'Description' => $onefeature->{'Description'},
+            'Display' => get_feature_display($onefeature),
+            'Level' => get_feature_level($onefeature),
+            'Directory_' => get_feature_directory($onefeature),
+            'Attributes' => get_feature_attributes($onefeature)
+        };
+        push @$features, $feature;
+
+        # collecting all feature in global feature collector (so that properties can be set in property table)
+        $installer::globals::featurecollector{$feature_gid} = 1;
+        
+        # collecting all language feature in feature collector for check of language selection
+        if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && $onefeature->{'ParentID'} ne $installer::globals::rootmodulegid)
+        {
+            $installer::globals::multilingual_only_modules{$feature_gid} = 1;
+        }
+
+        # collecting all application feature in global feature collector for check of application selection
+        if ( $styles =~ /\bAPPLICATIONMODULE\b/ )
+        {
+            $installer::globals::application_modules{$feature_gid} = 1;
+        }
+    }
+
+    return $features;
+}
+
+
+
+
+=head add_missing_features($features)
+
+    When we are building a release, then there may be features missing
+    that where present in the source release.  As missing features
+    would prevent patches from being created, we add the missing
+    features.
+
+    The returned feature hash is either identical to the given
+    $features or is a copy with the missing features added.
+
+=cut
+
+sub add_missing_features ($)
+{
+    my ($features) = @_;
+
+    return $features if ! $installer::globals::is_release;
+
+    # Aquire the feature list of the source release.
+    my $source_feature_table = $installer::globals::source_msi->GetTable("Feature");
+    my $feature_column_index = $source_feature_table->GetColumnIndex("Feature");
+
+    # Prepare fast lookup of the target features.
+    my %target_feature_map = map {$_->{'Feature'} => $_} @$features;
+    
+    # Find missing features.
+    my @missing_features = ();
+    foreach my $source_feature_row (@{$source_feature_table->GetAllRows()})
+    {
+        my $feature_gid = $source_feature_row->GetValue($feature_column_index);
+        if ( ! defined $target_feature_map{$feature_gid})
+        {
+            push @missing_features, $source_feature_row;
+        }
+    }
+
+    # Return when there are no missing features.
+    return $features if scalar @missing_features==0;
+    
+    # Process the missing features.
+    my $extended_features = [@$features];
+    foreach my $missing_feature_row (@missing_features)
+    {
+        my %feature = map
+            {$_ => $missing_feature_row->GetValue($_)}
+            ('Feature', 'Feature_Parent', 'Title', 'Description', 'Display', 'Level', 'Directory_', 'Attributes');
+        push @$extended_features, \%feature;
+        
+        $installer::logger::Lang->printf("added missing feature %s\n", $feature->{'Feature'}); 
+    }
+    return $extended_features;
+}
+
+
+
+
+sub create_feature_table ($$$)
+{
+	my ($basedir, $language, $features) = @_;
+
+    my @feature_table = ();
+    installer::windows::idtglobal::write_idt_header(\@feature_table, "feature");
+
+    foreach my $feature (@$features)
+    {
+        my $line = join("\t",
+            $feature->{'Feature'},
+            $feature->{'Feature_Parent'},
+            $feature->{'Title'},
+            $feature->{'Description'},
+            $feature->{'Display'},
+            $feature->{'Level'},
+            $feature->{'Directory_'},
+            $feature->{'Attributes'}) . "\n";
+
+        push(@feature_table, $line);
+    }
+
+    my $filename = $basedir . $installer::globals::separator . "Feature.idt" . "." . $language;
+    installer::files::save_file($filename ,\@feature_table);
+    $installer::logger::Lang->printf("Created idt file: %s\n", $filename); 
 }
 
 1;

Modified: openoffice/trunk/main/solenv/bin/modules/installer/windows/file.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/windows/file.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/windows/file.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/windows/file.pm Fri Dec 13 12:05:15 2013
@@ -635,19 +635,19 @@ sub assign_missing_sequence_numbers ($)
 
 sub create_items_for_missing_files ($$$)
 {
-    my ($missing_items, $msi, $directory_list) = @_;
+    my ($missing_items, $source_msi, $directory_list) = @_;
 
     # For creation of the FeatureComponent table (in a later step) we
     # have to provide references from the file to component and
     # modules (ie features).  Note that Each file belongs to exactly
     # one component but one component can belong to multiple features.
-    my $component_to_features_map = create_feature_component_map($msi);
+    my $component_to_features_map = create_feature_component_map($source_msi);
     
     my @new_files = ();
     foreach my $row (@$missing_items)
     {
         $installer::logger::Info->printf("creating new file item for '%s'\n", $row->GetValue('File'));
-        my $file_item = create_script_item_for_deleted_file($row, $msi, $component_to_features_map);
+        my $file_item = create_script_item_for_deleted_file($row, $source_msi, $component_to_features_map);
         push @new_files, $file_item;
     }
 
@@ -657,26 +657,53 @@ sub create_items_for_missing_files ($$$)
 
 
 
+=head2 create_script_item_for_deleted_file (($file_row, $source_msi, $component_to_features_map)
+
+    Create a new script item for a file that was present in the
+    previous release but isn't anymore.  Most of the necessary
+    information is taken from the 'File' table of the source release.
+
+    The values of 'sourcepath' and 'cyg_sourcepath' will point to the
+    respective file in the unpacked source release.  An alternative
+    would be to let them point to an empty file.  That, however, might
+    make the patch bigger (diff between identical file contents is
+    (almost) empty, diff between file and empty file is the 'inverse'
+    of the file).
+
+=cut
+
+my $use_source_files_for_missing_files = 1;
+
 sub create_script_item_for_deleted_file ($$$)
 {
-    my ($file_row, $msi, $component_to_features_map) = @_;
+    my ($file_row, $source_msi, $component_to_features_map) = @_;
 
     my $uniquename = $file_row->GetValue('File');
 
-    my $file_map = $msi->GetFileMap();
+    my $file_map = $source_msi->GetFileMap();
 
-    my $directory_item = $file_map->{$uniquename}->{'directory'};
+    my $file_item = $file_map->{$uniquename};
+    my $directory_item = $file_item->{'directory'};
     my $source_path = $directory_item->{'full_source_long_name'};
     my $target_path = $directory_item->{'full_target_long_name'};
-    my $full_source_name = File::Spec->catfile(
-        installer::patch::InstallationSet::GetUnpackedCabPath(
-            $msi->{'version'},
-            $msi->{'is_current_version'},
-            $msi->{'language'},
-            $msi->{'package_format'},
-            $msi->{'product_name'}),
-        $source_path,
-        $uniquename);
+    my $full_source_name = undef;
+    if ($use_source_files_for_missing_files)
+    {
+        $full_source_name = File::Spec->catfile(
+            installer::patch::InstallationSet::GetUnpackedCabPath(
+                $source_msi->{'version'},
+                $source_msi->{'is_current_version'},
+                $source_msi->{'language'},
+                $source_msi->{'package_format'},
+                $source_msi->{'product_name'}),
+            $source_path,
+            $file_item->{'long_name'});
+    }
+    else
+    {
+        $full_source_name = "/c/tmp/missing/".$uniquename;
+        installer::patch::Tools::touch($full_source_name);
+    }
     my ($long_name, undef) = installer::patch::Msi::SplitLongShortName($file_row->GetValue("FileName"));
     my $target_name = File::Spec->catfile($target_path, $long_name);
     if ( ! -f $full_source_name)

Modified: openoffice/trunk/main/solenv/bin/modules/installer/windows/msiglobal.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/windows/msiglobal.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/windows/msiglobal.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/windows/msiglobal.pm Fri Dec 13 12:05:15 2013
@@ -131,25 +131,6 @@ sub make_relative_ddf_path
 	return $sourcepath;
 }
 
-##########################################################################
-# Returning the order of the sequences in the files array.
-##########################################################################
-
-sub get_sequenceorder
-{
-	my ($filesref) = @_;
-
-	my %order = ();
-	
-	for ( my $i = 0; $i <= $#{$filesref}; $i++ )
-	{
-		my $onefile = ${$filesref}[$i];
-		if ( ! $onefile->{'assignedsequencenumber'} ) { installer::exiter::exit_program("ERROR: No sequence number assigned to $onefile->{'gid'} ($onefile->{'uniquename'})!", "get_sequenceorder"); }
-		$order{$onefile->{'assignedsequencenumber'}} = $i;
-	}
-	
-	return \%order;
-}
 
 ##########################################################################
 # Generation the list, in which the source of the files is connected
@@ -157,183 +138,85 @@ sub get_sequenceorder
 # to be included into a cab file, this has to be done via ddf files.
 ##########################################################################
 
-sub generate_cab_file_list
+sub generate_cab_file_list ($$$$)
 {
 	my ($filesref, $installdir, $ddfdir, $allvariables) = @_;
 	
-	my @cabfilelist = ();
-		
 	installer::logger::include_header_into_logfile("Generating ddf files");
 
-	$installer::logger::Lang->add_timestamp("Performance Info: ddf file generation start");
-
-	if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_pathes($filesref); }
-
-	if ( $installer::globals::fix_number_of_cab_files )
-	{
-		for ( my $i = 0; $i <= $#{$filesref}; $i++ )
-		{	
-			my $onefile = ${$filesref}[$i];
-			my $cabinetfile = $onefile->{'cabinet'};
-			my $sourcepath =  $onefile->{'sourcepath'};
-			if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
-			my $uniquename =  $onefile->{'uniquename'};
-
-			my $styles = "";
-			my $doinclude = 1;
-			if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
-			if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
-			
-
-			# to avoid lines with more than 256 characters, it can be useful to use relative pathes
-			if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
-
-			# all files with the same cabinetfile are directly behind each other in the files collector
-
-			my @ddffile = ();
-
-			write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
-	
-			my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
-			if ( $doinclude ) { push(@ddffile, $ddfline); }
-
-			my $nextfile = ${$filesref}[$i+1];
-			my $nextcabinetfile = "";
-		
-			if ( $nextfile->{'cabinet'} ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
-		
-			while ( $nextcabinetfile eq $cabinetfile )
-			{
-				$sourcepath =  $nextfile->{'sourcepath'};
-				if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; }
-				# to avoid lines with more than 256 characters, it can be useful to use relative pathes
-				if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
-				$uniquename =  $nextfile->{'uniquename'};
-				my $localdoinclude = 1;
-				my $nextfilestyles = "";				
-				if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; }
-				if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; }
-				$ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
-				if ( $localdoinclude ) { push(@ddffile, $ddfline); }
-				$i++;											# increasing the counter!
-				$nextfile = ${$filesref}[$i+1];
-				if ( $nextfile ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
-				else { $nextcabinetfile = "_lastfile_"; }
-			}
-		
-			# creating the DDF file
-
-			my $ddffilename = $cabinetfile;
-			$ddffilename =~ s/.cab/.ddf/;
-			$ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
-			$ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
-
-			installer::files::save_file($ddffilename ,\@ddffile);
-			my $infoline = "Created ddf file: $ddffilename\n"; 
-			$installer::logger::Lang->print($infoline);
-
-			# lines in ddf files must not be longer than 256 characters
-			check_ddf_file(\@ddffile, $ddffilename);
-
-			# Writing the makecab system call
-
-			my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n";
-		
-			push(@cabfilelist, $oneline);
+	if ( $^O =~ /cygwin/i )
+    {
+        installer::worker::generate_cygwin_pathes($filesref);
+    }
 
-			# collecting all ddf files
-			push(@installer::globals::allddffiles, $ddffilename);
-		}
-	}
-	elsif ( $installer::globals::one_cab_file )
-	{
-		my @ddffile = ();
-		
-		my $cabinetfile = "";
+    # Make sure that all files point to the same cabinet file.
+    # Multiple cabinet files are not supported anymore.
+    my $cabinetfile = $filesref->[0]->{'cabinet'};
+    foreach my $onefile (@$filesref)
+    {
+        if ($onefile->{'cabinet'} ne $cabinetfile)
+        {
+            installer::exiter::exit_program(
+                "ERROR: multiple cabinet files are not supported",
+                "generate_cab_file_list");
+        }
+    }
 
-		for ( my $i = 0; $i <= $#{$filesref}; $i++ )
-		{
-			my $onefile = ${$filesref}[$i];
-			$cabinetfile = $onefile->{'cabinet'};
-			my $sourcepath =  $onefile->{'sourcepath'};
-			if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
-			my $uniquename =  $onefile->{'uniquename'};
-
-			# to avoid lines with more than 256 characters, it can be useful to use relative pathes
-			if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
-
-			if ( $i == 0 ) { write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); }
-
-			my $styles = "";
-			my $doinclude = 1;
-			if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
-			if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
+    # Sort files on the sequence number.
+    my @sorted_files = sort {$a->{'sequencenumber'} <=> $b->{'sequencenumber'}} @$filesref;
 
-			my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
-			if ( $doinclude ) { push(@ddffile, $ddfline); }
-		}
-		
-		# creating the DDF file
+    my @ddffile = ();
+    write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
+    foreach my $onefile (@sorted_files)
+    {	
+        my $styles = $onefile->{'Styles'} // "";
+        if ($styles =~ /\bDONT_PACK\b/)
+        {
+            $installer::logger::Lang->printf("    excluding '%s' from ddf\n", $onefile->{'uniquename'});
+        }
+        
+        my $uniquename = $onefile->{'uniquename'};
+        my $sourcepath = $onefile->{'sourcepath'};
+        if ( $^O =~ /cygwin/i )
+        {
+            $sourcepath = $onefile->{'cyg_sourcepath'};
+        }
 
-		my $ddffilename = $cabinetfile;
-		$ddffilename =~ s/.cab/.ddf/;
-		$ddfdir =~ s/[\/\\]\s*$//;
-		$ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
+        # to avoid lines with more than 256 characters, it can be useful to use relative pathes
+        if ($allvariables->{'RELATIVE_PATHES_IN_DDF'})
+        {
+            $sourcepath = make_relative_ddf_path($sourcepath);
+        }
 
-		installer::files::save_file($ddffilename ,\@ddffile);
-		my $infoline = "Created ddf file: $ddffilename\n"; 
-		$installer::logger::Lang->print($infoline);
+        my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+        push(@ddffile, $ddfline);
 
-		# lines in ddf files must not be longer than 256 characters
-		check_ddf_file(\@ddffile, $ddffilename);
+        $installer::logger::Lang->printf("    adding '%s' with sequence %d to ddf\n",
+            $onefile->{'uniquename'},
+            $onefile->{'sequencenumber'});
+    }
+    # creating the DDF file
 
-		# Writing the makecab system call
+    my $ddffilename = $cabinetfile;
+    $ddffilename =~ s/.cab/.ddf/;
+    $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
+    $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
+    
+    installer::files::save_file($ddffilename ,\@ddffile);
+    $installer::logger::Lang->print("Created ddf file: %s\n", $ddffilename);
 
-		my $oneline = "makecab.exe /F " . $ddffilename . "\n";
-		
-		push(@cabfilelist, $oneline);
+    # lines in ddf files must not be longer than 256 characters
+    check_ddf_file(\@ddffile, $ddffilename);
 
-		# collecting all ddf files
-		push(@installer::globals::allddffiles, $ddffilename);
-	}
-	else
-	{
-		installer::exiter::exit_program("ERROR: No cab file specification in globals.pm !", "create_media_table");		
-	}
+    # collecting all ddf files
+    push(@installer::globals::allddffiles, $ddffilename);
 
-	$installer::logger::Lang->add_timestamp("Performance Info: ddf file generation end");
-		
-	return \@cabfilelist;	# contains all system calls for packaging process
+    # Writing the makecab system call
+    # Return a list with all system calls for packaging process.
+	my @cabfilelist = ("makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n");
+	return \@cabfilelist;
 }
 
-########################################################################
-# Returning the file sequence of a specified file.
-########################################################################
-
-sub get_file_sequence
-{
-	my ($filesref, $uniquefilename) = @_;
-
-	my $sequence = "";
-	my $found_sequence = 0;
-
-	for ( my $i = 0; $i <= $#{$filesref}; $i++ )
-	{
-		my $onefile = ${$filesref}[$i];
-		my $uniquename = $onefile->{'uniquename'};
-		
-		if ( $uniquename eq $uniquefilename )
-		{
-			$sequence = $onefile->{'sequencenumber'};			
-			$found_sequence = 1;
-			last;
-		}
-	}
-	
-	if ( ! $found_sequence ) { installer::exiter::exit_program("ERROR: No sequence found for $uniquefilename !", "get_file_sequence"); }
-	
-	return $sequence;
-}
 
 
 #################################################################

Modified: openoffice/trunk/main/solenv/bin/modules/installer/windows/property.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/windows/property.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/windows/property.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/windows/property.pm Fri Dec 13 12:05:15 2013
@@ -208,16 +208,14 @@ sub get_productversion_for_property_tabl
 # required for the Windows patch process.
 #######################################################
 
-sub set_featurename_properties_for_patch
+sub set_featurename_properties_for_patch ($)
 {
-	($propertyfile) = @_;
+	my ($propertyfile) = @_;
 	
-	for ( my $i = 0; $i <= $#installer::globals::featurecollector; $i++ )
+	foreach my $feature_gid (keys %installer::globals::featurecollector)
 	{
-		my $onepropertyline =  $installer::globals::featurecollector[$i] . "\t" . "1" . "\n";
-		push(@{$propertyfile}, $onepropertyline);
+		push @$propertyfile, $feature_gid . "\t" . "1" . "\n";
 	}
-
 }
 
 #######################################################

Modified: openoffice/trunk/main/solenv/bin/modules/installer/windows/registry.pm
URL: http://svn.apache.org/viewvc/openoffice/trunk/main/solenv/bin/modules/installer/windows/registry.pm?rev=1550699&r1=1550698&r2=1550699&view=diff
==============================================================================
--- openoffice/trunk/main/solenv/bin/modules/installer/windows/registry.pm (original)
+++ openoffice/trunk/main/solenv/bin/modules/installer/windows/registry.pm Fri Dec 13 12:05:15 2013
@@ -47,7 +47,10 @@ sub get_registry_component_name
 	my $componentname = "";
 	my $isrootmodule = 0;
 	
-	if ( $registryref->{'ModuleID'} ) { $componentname = $registryref->{'ModuleID'}; }
+	if ($registryref->{'ModuleID'})
+    {
+        $componentname = $registryref->{'ModuleID'};
+    }
 	
 	$componentname =~ s/\\/\_/g;
 	$componentname =~ s/\//\_/g;
@@ -56,7 +59,10 @@ sub get_registry_component_name
 
 	$componentname = lc($componentname);	# componentnames always lowercase
 
-	if ( $componentname eq "gid_module_root" ) { $isrootmodule = 1; }
+	if ( $componentname eq "gid_module_root" )
+    {
+        $isrootmodule = 1;
+    }
 
 	# Attention: Maximum length for the componentname is 72 
 	
@@ -69,20 +75,36 @@ sub get_registry_component_name
 	
 	# This componentname must be more specific
 	my $addon = "_";
-	if ( $allvariables->{'PRODUCTNAME'} ) { $addon = $addon . $allvariables->{'PRODUCTNAME'}; }
-	if ( $allvariables->{'PRODUCTVERSION'} ) { $addon = $addon . $allvariables->{'PRODUCTVERSION'}; }
+	if ($allvariables->{'PRODUCTNAME'})
+    {
+        $addon .= $allvariables->{'PRODUCTNAME'};
+    }
+
+    # Append the version number.
+    # Previously that was the full version number as provided by 'PRODUCTVERSION'.
+    # But MSI patches introduce the restriction that component names must not change.
+    # Use just the major version number.
+    my $version = $allvariables->{"BRANDPACKAGEVERSION"} // "";
+    $addon .= $version;
 	$addon = lc($addon);
 	$addon =~ s/ //g;
 	$addon =~ s/-//g;
 	$addon =~ s/\.//g;
 
-	my $styles = "";	
-	if ( $registryref->{'Styles'} ) { $styles = $registryref->{'Styles'}; }
-
 	$componentname = $componentname . $addon;
 
-	if (( $styles =~ /\bLANGUAGEPACK\b/ ) && ( $installer::globals::languagepack )) { $componentname = $componentname . "_lang"; }
-	if ( $styles =~ /\bALWAYS_REQUIRED\b/ ) { $componentname = $componentname . "_forced"; }
+	my $styles = $registryref->{'Styles'};
+	if (defined $styles)
+    {
+        if (($styles =~ /\bLANGUAGEPACK\b/) && $installer::globals::languagepack)
+        {
+            $componentname .= "_lang";
+        }
+        if ($styles =~ /\bALWAYS_REQUIRED\b/)
+        {
+            $componentname .= "_forced";
+        }
+    }
 
 	# Attention: Maximum length for the componentname is 72
 	# %installer::globals::allregistrycomponents_in_this_database_ : resetted for each database	
@@ -92,10 +114,13 @@ sub get_registry_component_name
 
 	my $fullname = $componentname;  # This can be longer than 72
 		
-	if (( exists($installer::globals::allregistrycomponents_{$fullname}) ) && ( ! exists($installer::globals::allregistrycomponents_in_this_database_{$fullname}) ))
+	if (exists($installer::globals::allregistrycomponents_{$fullname})
+        && ! exists($installer::globals::allregistrycomponents_in_this_database_{$fullname}))
 	{
 		# This is not allowed: One component cannot be installed with different packages.
-		installer::exiter::exit_program("ERROR: Windows registry component \"$fullname\" is already included into another package. This is not allowed.", "get_registry_component_name");
+		installer::exiter::exit_program(
+            "ERROR: Windows registry component \"$fullname\" is already included into another package. This is not allowed.",
+            "get_registry_component_name");
 	}
 		
 	if ( exists($installer::globals::allregistrycomponents_{$fullname}) )
@@ -113,7 +138,10 @@ sub get_registry_component_name
 		$installer::globals::allregistrycomponents_in_this_database_{$fullname} = 1;
 	}
 
-	if ( $isrootmodule ) { $installer::globals::registryrootcomponent = $componentname; }
+	if ( $isrootmodule )
+    {
+        $installer::globals::registryrootcomponent = $componentname;
+    }
 
 	return $componentname;	
 }