[Libreoffice-commits] core.git: Branch 'aoo/trunk' - instsetoo_native/util solenv/bin
Andre Fischer
af at apache.org
Fri Dec 13 06:08:10 PST 2013
instsetoo_native/util/makefile.mk | 41
solenv/bin/make_installer.pl | 17
solenv/bin/modules/installer/globals.pm | 2
solenv/bin/modules/installer/languages.pm | 13
solenv/bin/modules/installer/patch/InstallationSet.pm | 117 ++
solenv/bin/modules/installer/patch/Msi.pm | 127 +-
solenv/bin/modules/installer/patch/Tools.pm | 14
solenv/bin/modules/installer/windows/component.pm | 21
solenv/bin/modules/installer/windows/directory.pm | 2
solenv/bin/modules/installer/windows/feature.pm | 164 ++-
solenv/bin/modules/installer/windows/file.pm | 57 -
solenv/bin/modules/installer/windows/msiglobal.pm | 225 +---
solenv/bin/modules/installer/windows/property.pm | 10
solenv/bin/modules/installer/windows/registry.pm | 52 -
solenv/bin/patch_tool.pl | 819 +++++++++++++-----
solenv/bin/release_prepare.pl | 2
16 files changed, 1122 insertions(+), 561 deletions(-)
New commits:
commit 20fcc36e1c2f22d824d478d39bb37d5162317526
Author: Andre Fischer <af at apache.org>
Date: Fri Dec 13 12:05:15 2013 +0000
123531: Fixes for the patch creation.
diff --git a/instsetoo_native/util/makefile.mk b/instsetoo_native/util/makefile.mk
index 0988ebd0..3a9e3f5 100644
--- a/instsetoo_native/util/makefile.mk
+++ b/instsetoo_native/util/makefile.mk
@@ -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,L,$(alllangiso) openoffice_$L.$P)) .PHO
$(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,L,$(alllangiso) sdkoo_$L.$P)) .PHONY :
$(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 $@
diff --git a/solenv/bin/make_installer.pl b/solenv/bin/make_installer.pl
index 8aa7449..f73c0b6 100644
--- a/solenv/bin/make_installer.pl
+++ b/solenv/bin/make_installer.pl
@@ -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,
diff --git a/solenv/bin/modules/installer/globals.pm b/solenv/bin/modules/installer/globals.pm
index 603991a..0afa9af 100644
--- a/solenv/bin/modules/installer/globals.pm
+++ b/solenv/bin/modules/installer/globals.pm
@@ -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";
diff --git a/solenv/bin/modules/installer/languages.pm b/solenv/bin/modules/installer/languages.pm
index 13b0736..5b83ce0 100644
--- a/solenv/bin/modules/installer/languages.pm
+++ b/solenv/bin/modules/installer/languages.pm
@@ -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 =~ /^.*?_(.*)$/)
diff --git a/solenv/bin/modules/installer/patch/InstallationSet.pm b/solenv/bin/modules/installer/patch/InstallationSet.pm
index 876bfb8..8ea4f15 100644
--- a/solenv/bin/modules/installer/patch/InstallationSet.pm
+++ b/solenv/bin/modules/installer/patch/InstallationSet.pm
@@ -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);
+
+ # Process the response.
+ my $path_to_7zip = undef;
+ if ($response =~ /\s+REG_SZ\s+([^\r\n]*)/m)
+ {
+ $path_to_7zip = $1;
+ }
- open my $out, ">", $filename;
- close $out;
+ # 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,6 +176,7 @@ 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
@@ -130,22 +188,30 @@ sub UnpackCab ($$$)
# 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,6 +515,7 @@ 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'} =~ /^(.*)\/([^\/]+)$/;
@@ -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;
}
diff --git a/solenv/bin/modules/installer/patch/Msi.pm b/solenv/bin/modules/installer/patch/Msi.pm
index e5b47f6..f6d4497 100644
--- a/solenv/bin/modules/installer/patch/Msi.pm
+++ b/solenv/bin/modules/installer/patch/Msi.pm
@@ -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
};
}
diff --git a/solenv/bin/modules/installer/patch/Tools.pm b/solenv/bin/modules/installer/patch/Tools.pm
index 06035a3..6dd45e9 100644
--- a/solenv/bin/modules/installer/patch/Tools.pm
+++ b/solenv/bin/modules/installer/patch/Tools.pm
@@ -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;
diff --git a/solenv/bin/modules/installer/windows/component.pm b/solenv/bin/modules/installer/windows/component.pm
index 8ccaed7..0383852 100644
--- a/solenv/bin/modules/installer/windows/component.pm
+++ b/solenv/bin/modules/installer/windows/component.pm
@@ -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;
diff --git a/solenv/bin/modules/installer/windows/directory.pm b/solenv/bin/modules/installer/windows/directory.pm
index 5560260..c62f50c 100644
--- a/solenv/bin/modules/installer/windows/directory.pm
+++ b/solenv/bin/modules/installer/windows/directory.pm
@@ -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'},
diff --git a/solenv/bin/modules/installer/windows/feature.pm b/solenv/bin/modules/installer/windows/feature.pm
index 20cba14..d2a1aa4 100644
--- a/solenv/bin/modules/installer/windows/feature.pm
+++ b/solenv/bin/modules/installer/windows/feature.pm
@@ -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 $features = [];
+
+ foreach my $onefeature (@$modules)
{
- my $onelanguage = ${$languagesarrayref}[$m];
+ # 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;
+ }
- my $infoline;
+ # collecting all application feature in global feature collector for check of application selection
+ if ( $styles =~ /\bAPPLICATIONMODULE\b/ )
+ {
+ $installer::globals::application_modules{$feature_gid} = 1;
+ }
+ }
- my @featuretable = ();
+ return $features;
+}
- 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
+=head add_missing_features($features)
- if (! (!(( $onefeature->{'ismultilingual'} )) || ( $onefeature->{'specificlanguage'} eq $onelanguage )) ) { next; }
+ 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.
- my %feature = ();
+ The returned feature hash is either identical to the given
+ $features or is a copy with the missing features added.
- $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);
+=cut
- 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";
+sub add_missing_features ($)
+{
+ my ($features) = @_;
- push(@featuretable, $oneline);
+ return $features if ! $installer::globals::is_release;
- # 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'});
- }
+ # 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");
- # 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;
- }
+ # Prepare fast lookup of the target features.
+ my %target_feature_map = map {$_->{'Feature'} => $_} @$features;
- # collecting all application feature in global feature collector for check of application selection
- if ( $styles =~ /\bAPPLICATIONMODULE\b/ )
- {
- $installer::globals::application_modules{$feature{'feature'}} = 1;
- }
+ # 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;
}
+ }
- # Saving the file
+ # 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;
- 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);
+ $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;
diff --git a/solenv/bin/modules/installer/windows/file.pm b/solenv/bin/modules/installer/windows/file.pm
index e9b5d76..339cf4a 100644
--- a/solenv/bin/modules/installer/windows/file.pm
+++ b/solenv/bin/modules/installer/windows/file.pm
@@ -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)
diff --git a/solenv/bin/modules/installer/windows/msiglobal.pm b/solenv/bin/modules/installer/windows/msiglobal.pm
index 81c0b31..57c06fc 100644
--- a/solenv/bin/modules/installer/windows/msiglobal.pm
+++ b/solenv/bin/modules/installer/windows/msiglobal.pm
@@ -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,185 +138,87 @@ 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 ( $^O =~ /cygwin/i )
+ {
+ installer::worker::generate_cygwin_pathes($filesref);
+ }
- if ( $installer::globals::fix_number_of_cab_files )
+ # 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)
{
- for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ if ($onefile->{'cabinet'} ne $cabinetfile)
{
- 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);
-
- # collecting all ddf files
- push(@installer::globals::allddffiles, $ddffilename);
+ installer::exiter::exit_program(
+ "ERROR: multiple cabinet files are not supported",
+ "generate_cab_file_list");
}
}
- elsif ( $installer::globals::one_cab_file )
- {
- my @ddffile = ();
- my $cabinetfile = "";
+ # Sort files on the sequence number.
+ my @sorted_files = sort {$a->{'sequencenumber'} <=> $b->{'sequencenumber'}} @$filesref;
- for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ my @ddffile = ();
+ write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
+ foreach my $onefile (@sorted_files)
+ {
+ my $styles = $onefile->{'Styles'} // "";
+ if ($styles =~ /\bDONT_PACK\b/)
{
- 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; }
-
- my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
- if ( $doinclude ) { push(@ddffile, $ddfline); }
+ $installer::logger::Lang->printf(" excluding '%s' from ddf\n", $onefile->{'uniquename'});
}
- # creating the DDF file
-
- my $ddffilename = $cabinetfile;
- $ddffilename =~ s/.cab/.ddf/;
- $ddfdir =~ s/[\/\\]\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 $uniquename = $onefile->{'uniquename'};
+ my $sourcepath = $onefile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i )
+ {
+ $sourcepath = $onefile->{'cyg_sourcepath'};
+ }
- my $oneline = "makecab.exe /F " . $ddffilename . "\n";
+ # 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);
+ }
- push(@cabfilelist, $oneline);
+ my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+ push(@ddffile, $ddfline);
- # collecting all ddf files
- push(@installer::globals::allddffiles, $ddffilename);
+ $installer::logger::Lang->printf(" adding '%s' with sequence %d to ddf\n",
+ $onefile->{'uniquename'},
+ $onefile->{'sequencenumber'});
}
- else
- {
- installer::exiter::exit_program("ERROR: No cab file specification in globals.pm !", "create_media_table");
- }
-
- $installer::logger::Lang->add_timestamp("Performance Info: ddf file generation end");
-
- return \@cabfilelist; # contains all system calls for packaging process
-}
-
-########################################################################
-# Returning the file sequence of a specified file.
-########################################################################
-
-sub get_file_sequence
-{
- my ($filesref, $uniquefilename) = @_;
+ # creating the DDF file
- my $sequence = "";
- my $found_sequence = 0;
+ my $ddffilename = $cabinetfile;
+ $ddffilename =~ s/.cab/.ddf/;
+ $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
+ $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
- for ( my $i = 0; $i <= $#{$filesref}; $i++ )
- {
- my $onefile = ${$filesref}[$i];
- my $uniquename = $onefile->{'uniquename'};
+ installer::files::save_file($ddffilename ,\@ddffile);
+ $installer::logger::Lang->print("Created ddf file: %s\n", $ddffilename);
- if ( $uniquename eq $uniquefilename )
- {
- $sequence = $onefile->{'sequencenumber'};
- $found_sequence = 1;
- last;
- }
- }
+ # lines in ddf files must not be longer than 256 characters
+ check_ddf_file(\@ddffile, $ddffilename);
- if ( ! $found_sequence ) { installer::exiter::exit_program("ERROR: No sequence found for $uniquefilename !", "get_file_sequence"); }
+ # collecting all ddf files
+ push(@installer::globals::allddffiles, $ddffilename);
- return $sequence;
+ # 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 name of the msi database
#################################################################
diff --git a/solenv/bin/modules/installer/windows/property.pm b/solenv/bin/modules/installer/windows/property.pm
index e93ad04..e6b0d9b 100644
--- a/solenv/bin/modules/installer/windows/property.pm
+++ b/solenv/bin/modules/installer/windows/property.pm
@@ -208,16 +208,14 @@ sub get_productversion_for_property_table
# 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";
}
-
}
#######################################################
diff --git a/solenv/bin/modules/installer/windows/registry.pm b/solenv/bin/modules/installer/windows/registry.pm
index f277e47..1352d28 100644
--- a/solenv/bin/modules/installer/windows/registry.pm
+++ b/solenv/bin/modules/installer/windows/registry.pm
@@ -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;
}
diff --git a/solenv/bin/patch_tool.pl b/solenv/bin/patch_tool.pl
index e490df5..c82806c 100644
--- a/solenv/bin/patch_tool.pl
+++ b/solenv/bin/patch_tool.pl
@@ -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");
+ # 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()};
- return 1;
+ 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)
+ $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)
{
- if ($componentname !~ /^registry/)
- {
- $is_file_component_removed = 1;
- }
+ 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 ($is_file_component_removed)
+ }
+ if (scalar @feature_names == 0)
{
- $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;
+ $installer::logger::Info->printf("Error: no feature found for component '%s'\n", $component_name);
+ $error = 1;
}
else
{
- $installer::logger::Info->printf(
- "Warning: %d components have been addded, all of them registry components\n",
- scalar @added_components);
+ # 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 !$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;
- }
+ $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 $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 $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",
+ 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);
+ }
}
diff --git a/solenv/bin/release_prepare.pl b/solenv/bin/release_prepare.pl
index 1d22ad7..432adf2 100644
--- a/solenv/bin/release_prepare.pl
+++ b/solenv/bin/release_prepare.pl
@@ -32,7 +32,7 @@ use Getopt::Long;
use Pod::Usage;
use Digest;
-use Carp::Always;
+#use Carp::Always;
use strict;
More information about the Libreoffice-commits
mailing list