19f91b7e3SAndre Fischer#!/usr/bin/perl -w 29f91b7e3SAndre Fischer 39f91b7e3SAndre Fischer#************************************************************** 49f91b7e3SAndre Fischer# 59f91b7e3SAndre Fischer# Licensed to the Apache Software Foundation (ASF) under one 69f91b7e3SAndre Fischer# or more contributor license agreements. See the NOTICE file 79f91b7e3SAndre Fischer# distributed with this work for additional information 89f91b7e3SAndre Fischer# regarding copyright ownership. The ASF licenses this file 99f91b7e3SAndre Fischer# to you under the Apache License, Version 2.0 (the 109f91b7e3SAndre Fischer# "License"); you may not use this file except in compliance 119f91b7e3SAndre Fischer# with the License. You may obtain a copy of the License at 129f91b7e3SAndre Fischer# 139f91b7e3SAndre Fischer# http://www.apache.org/licenses/LICENSE-2.0 149f91b7e3SAndre Fischer# 159f91b7e3SAndre Fischer# Unless required by applicable law or agreed to in writing, 169f91b7e3SAndre Fischer# software distributed under the License is distributed on an 179f91b7e3SAndre Fischer# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 189f91b7e3SAndre Fischer# KIND, either express or implied. See the License for the 199f91b7e3SAndre Fischer# specific language governing permissions and limitations 209f91b7e3SAndre Fischer# under the License. 219f91b7e3SAndre Fischer# 229f91b7e3SAndre Fischer#************************************************************** 239f91b7e3SAndre Fischer 249f91b7e3SAndre Fischeruse Getopt::Long; 259f91b7e3SAndre Fischeruse Pod::Usage; 269f91b7e3SAndre Fischeruse File::Path; 279f91b7e3SAndre Fischeruse File::Spec; 289f91b7e3SAndre Fischeruse File::Basename; 299f91b7e3SAndre Fischeruse XML::LibXML; 309f91b7e3SAndre Fischeruse Digest; 319f91b7e3SAndre Fischeruse Archive::Zip; 329f91b7e3SAndre Fischeruse Archive::Extract; 339f91b7e3SAndre Fischer 349f91b7e3SAndre Fischeruse installer::ziplist; 359f91b7e3SAndre Fischeruse installer::logger; 369f91b7e3SAndre Fischeruse installer::windows::msiglobal; 379f91b7e3SAndre Fischeruse installer::patch::Msi; 389f91b7e3SAndre Fischeruse installer::patch::ReleasesList; 399f91b7e3SAndre Fischeruse installer::patch::Version; 409f91b7e3SAndre Fischer 419f91b7e3SAndre Fischeruse strict; 429f91b7e3SAndre Fischer 439f91b7e3SAndre Fischer 449f91b7e3SAndre Fischer=head1 NAME 459f91b7e3SAndre Fischer 469f91b7e3SAndre Fischer patch_tool.pl - Create Windows MSI patches. 479f91b7e3SAndre Fischer 489f91b7e3SAndre Fischer=head1 SYNOPSIS 499f91b7e3SAndre Fischer 509f91b7e3SAndre Fischer patch_tool.pl command [options] 519f91b7e3SAndre Fischer 529f91b7e3SAndre Fischer Commands: 539f91b7e3SAndre Fischer create create patches 549f91b7e3SAndre Fischer apply apply patches 559f91b7e3SAndre Fischer 569f91b7e3SAndre Fischer Options: 579f91b7e3SAndre Fischer -p|--product-name <product-name> 589f91b7e3SAndre Fischer The product name, eg Apache_OpenOffice 599f91b7e3SAndre Fischer -o|--output-path <path> 609f91b7e3SAndre Fischer Path to the instsetoo_native platform output tree 619f91b7e3SAndre Fischer -d|--data-path <path> 629f91b7e3SAndre Fischer Path to the data directory that is expected to be under version control. 639f91b7e3SAndre Fischer --source-version <major>.<minor>.<micro> 649f91b7e3SAndre Fischer The version that is to be patched. 659f91b7e3SAndre Fischer --target-version <major>.<minor>.<micro> 669f91b7e3SAndre Fischer The version after the patch has been applied. 679f91b7e3SAndre Fischer 689f91b7e3SAndre Fischer=head1 DESCRIPTION 699f91b7e3SAndre Fischer 709f91b7e3SAndre Fischer Creates windows MSP patch files, one for each relevant language. 719f91b7e3SAndre Fischer Patches convert an installed OpenOffice to the target version. 729f91b7e3SAndre Fischer 739f91b7e3SAndre Fischer Required data are: 749f91b7e3SAndre Fischer Installation sets of the source versions 759f91b7e3SAndre Fischer Taken from ext_sources/ 769f91b7e3SAndre Fischer Downloaded from archive.apache.org on demand 779f91b7e3SAndre Fischer 789f91b7e3SAndre Fischer Installation set of the target version 799f91b7e3SAndre Fischer This is expected to be the current version. 809f91b7e3SAndre Fischer 819f91b7e3SAndre Fischer=cut 829f91b7e3SAndre Fischer 839f91b7e3SAndre Fischer# my $ImageFamily = "MNPapps"; 849f91b7e3SAndre Fischer# The ImageFamily name has to have 1-8 alphanumeric characters. 859f91b7e3SAndre Fischermy $ImageFamily = "AOO"; 869f91b7e3SAndre Fischermy $SourceImageName = "Source"; 879f91b7e3SAndre Fischermy $TargetImageName = "Target"; 889f91b7e3SAndre Fischer 899f91b7e3SAndre Fischer 909f91b7e3SAndre Fischer 919f91b7e3SAndre Fischersub ProcessCommandline () 929f91b7e3SAndre Fischer{ 939f91b7e3SAndre Fischer my $arguments = { 949f91b7e3SAndre Fischer 'product-name' => undef, 959f91b7e3SAndre Fischer 'output-path' => undef, 969f91b7e3SAndre Fischer 'data-path' => undef, 979f91b7e3SAndre Fischer 'lst-file' => undef, 989f91b7e3SAndre Fischer 'source-version' => undef, 999f91b7e3SAndre Fischer 'target-version' => undef}; 1009f91b7e3SAndre Fischer 1019f91b7e3SAndre Fischer if ( ! GetOptions( 1029f91b7e3SAndre Fischer "product-name=s", \$arguments->{'product-name'}, 1039f91b7e3SAndre Fischer "output-path=s", \$arguments->{'output-path'}, 1049f91b7e3SAndre Fischer "data-path=s" => \$arguments->{'data-path'}, 1059f91b7e3SAndre Fischer "lst-file=s" => \$arguments->{'lst-file'}, 1069f91b7e3SAndre Fischer "source-version:s" => \$arguments->{'source-version'}, 1079f91b7e3SAndre Fischer "target-version:s" => \$arguments->{'target-version'} 1089f91b7e3SAndre Fischer )) 1099f91b7e3SAndre Fischer { 1109f91b7e3SAndre Fischer pod2usage(2); 1119f91b7e3SAndre Fischer } 1129f91b7e3SAndre Fischer 1139f91b7e3SAndre Fischer # Only the command should be left in @ARGV. 1149f91b7e3SAndre Fischer pod2usage(2) unless scalar @ARGV == 1; 1159f91b7e3SAndre Fischer $arguments->{'command'} = shift @ARGV; 1169f91b7e3SAndre Fischer 1179f91b7e3SAndre Fischer # At the moment we only support patches on windows. When this 1189f91b7e3SAndre Fischer # is extended in the future we need the package format as an 1199f91b7e3SAndre Fischer # argument. 1209f91b7e3SAndre Fischer $arguments->{'package-format'} = "msi"; 1219f91b7e3SAndre Fischer 1229f91b7e3SAndre Fischer return $arguments; 1239f91b7e3SAndre Fischer} 1249f91b7e3SAndre Fischer 1259f91b7e3SAndre Fischer 1269f91b7e3SAndre Fischer 1279f91b7e3SAndre Fischer 1289f91b7e3SAndre Fischersub GetSourceMsiPath ($$) 1299f91b7e3SAndre Fischer{ 1309f91b7e3SAndre Fischer my ($context, $language) = @_; 1319f91b7e3SAndre Fischer my $unpacked_path = File::Spec->catfile( 1329f91b7e3SAndre Fischer $context->{'output-path'}, 1339f91b7e3SAndre Fischer $context->{'product-name'}, 1349f91b7e3SAndre Fischer $context->{'package-format'}, 1359f91b7e3SAndre Fischer installer::patch::Version::ArrayToDirectoryName( 1369f91b7e3SAndre Fischer installer::patch::Version::StringToNumberArray( 1379f91b7e3SAndre Fischer $context->{'source-version'})), 1389f91b7e3SAndre Fischer $language); 1399f91b7e3SAndre Fischer} 1409f91b7e3SAndre Fischer 1419f91b7e3SAndre Fischer 1429f91b7e3SAndre Fischer 1439f91b7e3SAndre Fischer 1449f91b7e3SAndre Fischersub GetTargetMsiPath ($$) 1459f91b7e3SAndre Fischer{ 1469f91b7e3SAndre Fischer my ($context, $language) = @_; 1479f91b7e3SAndre Fischer return File::Spec->catfile( 1489f91b7e3SAndre Fischer $context->{'output-path'}, 1499f91b7e3SAndre Fischer $context->{'product-name'}, 1509f91b7e3SAndre Fischer $context->{'package-format'}, 1519f91b7e3SAndre Fischer "install", 1529f91b7e3SAndre Fischer $language); 1539f91b7e3SAndre Fischer} 1549f91b7e3SAndre Fischer 1559f91b7e3SAndre Fischer 1569f91b7e3SAndre Fischer 1579f91b7e3SAndre Fischersub ProvideInstallationSets ($$) 1589f91b7e3SAndre Fischer{ 1599f91b7e3SAndre Fischer my ($context, $language) = @_; 1609f91b7e3SAndre Fischer 1619f91b7e3SAndre Fischer # Assume that the target installation set is located in the output tree. 1629f91b7e3SAndre Fischer my $target_path = GetTargetMsiPath($context, $language); 1639f91b7e3SAndre Fischer if ( ! -d $target_path) 1649f91b7e3SAndre Fischer { 1659f91b7e3SAndre Fischer installer::logger::PrintError("can not find target installation set at '%s'\n", $target_path); 1669f91b7e3SAndre Fischer return 0; 1679f91b7e3SAndre Fischer } 1689f91b7e3SAndre Fischer my @target_version = installer::patch::Version::StringToNumberArray($context->{'target-version'}); 1699f91b7e3SAndre Fischer my $target_msi_file = File::Spec->catfile( 1709f91b7e3SAndre Fischer $target_path, 1719f91b7e3SAndre Fischer sprintf("openoffice%d%d%d.msi", $target_version[0], $target_version[1], $target_version[2])); 1729f91b7e3SAndre Fischer if ( ! -f $target_msi_file) 1739f91b7e3SAndre Fischer { 1749f91b7e3SAndre Fischer installer::logger::PrintError("can not find target msi file at '%s'\n", $target_msi_file); 1759f91b7e3SAndre Fischer return 0; 1769f91b7e3SAndre Fischer } 1779f91b7e3SAndre Fischer 1789f91b7e3SAndre Fischer return 1; 1799f91b7e3SAndre Fischer} 1809f91b7e3SAndre Fischer 1819f91b7e3SAndre Fischer 1829f91b7e3SAndre Fischer 1839f91b7e3SAndre Fischer 1849f91b7e3SAndre Fischersub GetLanguages () 1859f91b7e3SAndre Fischer{ 1869f91b7e3SAndre Fischer # The set of languages is taken from the WITH_LANG environment variable. 1879f91b7e3SAndre Fischer # If that is missing or is empty then the default 'en-US' is used instead. 1889f91b7e3SAndre Fischer my @languages = ("en-US"); 1899f91b7e3SAndre Fischer my $with_lang = $ENV{'WITH_LANG'}; 1909f91b7e3SAndre Fischer if (defined $with_lang && $with_lang ne "") 1919f91b7e3SAndre Fischer { 1929f91b7e3SAndre Fischer @languages = split(/\s+/, $with_lang); 1939f91b7e3SAndre Fischer } 1949f91b7e3SAndre Fischer return @languages; 1959f91b7e3SAndre Fischer} 1969f91b7e3SAndre Fischer 1979f91b7e3SAndre Fischer 1989f91b7e3SAndre Fischer 1999f91b7e3SAndre Fischer 2009f91b7e3SAndre Fischersub FindValidLanguages ($$$) 2019f91b7e3SAndre Fischer{ 2029f91b7e3SAndre Fischer my ($context, $release_data, $languages) = @_; 2039f91b7e3SAndre Fischer 2049f91b7e3SAndre Fischer my @valid_languages = (); 2059f91b7e3SAndre Fischer foreach my $language (@$languages) 2069f91b7e3SAndre Fischer { 2079f91b7e3SAndre Fischer if ( ! ProvideInstallationSets($context, $language)) 2089f91b7e3SAndre Fischer { 2099f91b7e3SAndre Fischer installer::logger::PrintError(" '%s' has no target installation set\n", $language); 2109f91b7e3SAndre Fischer } 2119f91b7e3SAndre Fischer elsif ( ! defined $release_data->{$language}) 2129f91b7e3SAndre Fischer { 2139f91b7e3SAndre Fischer installer::logger::PrintError(" '%s' is not a released language for version %s\n", 2149f91b7e3SAndre Fischer $language, 2159f91b7e3SAndre Fischer $context->{'source-version'}); 2169f91b7e3SAndre Fischer } 2179f91b7e3SAndre Fischer else 2189f91b7e3SAndre Fischer { 2199f91b7e3SAndre Fischer push @valid_languages, $language; 2209f91b7e3SAndre Fischer } 2219f91b7e3SAndre Fischer } 2229f91b7e3SAndre Fischer 2239f91b7e3SAndre Fischer return @valid_languages; 2249f91b7e3SAndre Fischer} 2259f91b7e3SAndre Fischer 2269f91b7e3SAndre Fischer 2279f91b7e3SAndre Fischer 2289f91b7e3SAndre Fischer 2299f91b7e3SAndre Fischersub ProvideSourceInstallationSet ($$$) 2309f91b7e3SAndre Fischer{ 2319f91b7e3SAndre Fischer my ($context, $language, $release_data) = @_; 2329f91b7e3SAndre Fischer 2339f91b7e3SAndre Fischer my $url = $release_data->{$language}->{'URL'}; 2349f91b7e3SAndre Fischer $url =~ /^(.*)\/([^\/]*)$/; 2359f91b7e3SAndre Fischer my ($location, $basename) = ($1,$2); 2369f91b7e3SAndre Fischer 2379f91b7e3SAndre Fischer my $ext_sources_path = $ENV{'TARFILE_LOCATION'}; 2389f91b7e3SAndre Fischer if ( ! -d $ext_sources_path) 2399f91b7e3SAndre Fischer { 2409f91b7e3SAndre Fischer installer::logger::PrintError("Can not determine the path to ext_sources/.\n"); 2419f91b7e3SAndre Fischer installer::logger::PrintError("Maybe SOURCE_ROOT_DIR has not been correctly set in the environment?"); 2429f91b7e3SAndre Fischer return 0; 2439f91b7e3SAndre Fischer } 2449f91b7e3SAndre Fischer 2459f91b7e3SAndre Fischer # We need the unpacked installation set in <platform>/<product>/<package>/<source-version>, 2469f91b7e3SAndre Fischer # eg wntmsci12.pro/Apache_OpenOffice/msi/v-4-0-0. 2479f91b7e3SAndre Fischer my $unpacked_path = GetSourceMsiPath($context, $language); 2489f91b7e3SAndre Fischer if ( ! -d $unpacked_path) 2499f91b7e3SAndre Fischer { 2509f91b7e3SAndre Fischer # Make sure that the downloadable installation set (.exe) is present in ext_sources/. 2519f91b7e3SAndre Fischer my $filename = File::Spec->catfile($ext_sources_path, $basename); 2529f91b7e3SAndre Fischer if ( -f $filename) 2539f91b7e3SAndre Fischer { 2549f91b7e3SAndre Fischer PrintInfo("%s is already present in ext_sources/. Nothing to do\n", $basename); 2559f91b7e3SAndre Fischer } 2569f91b7e3SAndre Fischer else 2579f91b7e3SAndre Fischer { 258*d575d58fSAndre Fischer return 0 if ! installer::patch::InstallationSet::Download( 259*d575d58fSAndre Fischer $language, 260*d575d58fSAndre Fischer $release_data, 261*d575d58fSAndre Fischer $filename); 2629f91b7e3SAndre Fischer return 0 if ! -f $filename; 2639f91b7e3SAndre Fischer } 2649f91b7e3SAndre Fischer 2659f91b7e3SAndre Fischer # Unpack the installation set. 2669f91b7e3SAndre Fischer if ( -d $unpacked_path) 2679f91b7e3SAndre Fischer { 2689f91b7e3SAndre Fischer # Take the existence of the destination path as proof that the 2699f91b7e3SAndre Fischer # installation set was successfully unpacked before. 2709f91b7e3SAndre Fischer } 2719f91b7e3SAndre Fischer else 2729f91b7e3SAndre Fischer { 2739f91b7e3SAndre Fischer installer::patch::InstallationSet::Unpack($filename, $unpacked_path); 2749f91b7e3SAndre Fischer } 2759f91b7e3SAndre Fischer } 2769f91b7e3SAndre Fischer} 2779f91b7e3SAndre Fischer 2789f91b7e3SAndre Fischer 2799f91b7e3SAndre Fischer 2809f91b7e3SAndre Fischer 2819f91b7e3SAndre Fischer# Find the source and target version between which the patch will be 2829f91b7e3SAndre Fischer# created. Typically the target version is the current version and 2839f91b7e3SAndre Fischer# the source version is the version of the previous release. 2849f91b7e3SAndre Fischersub DetermineVersions ($$) 2859f91b7e3SAndre Fischer{ 2869f91b7e3SAndre Fischer my ($context, $variables) = @_; 2879f91b7e3SAndre Fischer 2889f91b7e3SAndre Fischer if (defined $context->{'source-version'} && defined $context->{'target-version'}) 2899f91b7e3SAndre Fischer { 2909f91b7e3SAndre Fischer # Both source and target version have been specified on the 2919f91b7e3SAndre Fischer # command line. There remains nothing to be be done. 2929f91b7e3SAndre Fischer return; 2939f91b7e3SAndre Fischer } 2949f91b7e3SAndre Fischer 2959f91b7e3SAndre Fischer if ( ! defined $context->{'target-version'}) 2969f91b7e3SAndre Fischer { 2979f91b7e3SAndre Fischer # Use the current version as target version. 2989f91b7e3SAndre Fischer $context->{'target-version'} = $variables->{PRODUCTVERSION}; 2999f91b7e3SAndre Fischer } 3009f91b7e3SAndre Fischer 3019f91b7e3SAndre Fischer my @target_version = installer::patch::Version::StringToNumberArray($context->{'target-version'}); 3029f91b7e3SAndre Fischer shift @target_version; 3039f91b7e3SAndre Fischer my $is_target_version_major = 1; 3049f91b7e3SAndre Fischer foreach my $number (@target_version) 3059f91b7e3SAndre Fischer { 3069f91b7e3SAndre Fischer $is_target_version_major = 0 if ($number ne "0"); 3079f91b7e3SAndre Fischer } 3089f91b7e3SAndre Fischer if ($is_target_version_major) 3099f91b7e3SAndre Fischer { 3109f91b7e3SAndre Fischer installer::logger::PrintError("can not create patch where target version is a new major version (%s)\n", 3119f91b7e3SAndre Fischer $context->{'target-version'}); 3129f91b7e3SAndre Fischer die; 3139f91b7e3SAndre Fischer } 3149f91b7e3SAndre Fischer 3159f91b7e3SAndre Fischer if ( ! defined $context->{'source-version'}) 3169f91b7e3SAndre Fischer { 3179f91b7e3SAndre Fischer my $releases = installer::patch::ReleasesList::Instance(); 3189f91b7e3SAndre Fischer 3199f91b7e3SAndre Fischer # Search for target release in the list of previous releases. 3209f91b7e3SAndre Fischer # If it is found, use the previous version as source version. 3219f91b7e3SAndre Fischer # Otherwise use the last released version. 3229f91b7e3SAndre Fischer my $last_release = undef; 3239f91b7e3SAndre Fischer foreach my $release (@{$releases->{'releases'}}) 3249f91b7e3SAndre Fischer { 3259f91b7e3SAndre Fischer last if ($release eq $context->{'target-version'}); 3269f91b7e3SAndre Fischer $last_release = $release; 3279f91b7e3SAndre Fischer } 3289f91b7e3SAndre Fischer $context->{'source-version'} = $last_release; 3299f91b7e3SAndre Fischer } 3309f91b7e3SAndre Fischer} 3319f91b7e3SAndre Fischer 3329f91b7e3SAndre Fischer 3339f91b7e3SAndre Fischer 3349f91b7e3SAndre Fischer 3359f91b7e3SAndre Fischer=head2 CheckUpgradeCode($source_msi, $target_msi) 3369f91b7e3SAndre Fischer 3379f91b7e3SAndre Fischer The 'UpgradeCode' values in the 'Property' table differs from source to target 3389f91b7e3SAndre Fischer 3399f91b7e3SAndre Fischer=cut 3409f91b7e3SAndre Fischersub CheckUpgradeCode($$) 3419f91b7e3SAndre Fischer{ 3429f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 3439f91b7e3SAndre Fischer 3449f91b7e3SAndre Fischer my $source_upgrade_code = $source_msi->GetTable("Property")->GetValue("Property", "UpgradeCode", "Value"); 3459f91b7e3SAndre Fischer my $target_upgrade_code = $target_msi->GetTable("Property")->GetValue("Property", "UpgradeCode", "Value"); 3469f91b7e3SAndre Fischer 3479f91b7e3SAndre Fischer if ($source_upgrade_code eq $target_upgrade_code) 3489f91b7e3SAndre Fischer { 3499f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: The UpgradeCode properties have to differ but are both '%s'\n", 3509f91b7e3SAndre Fischer $source_upgrade_code); 3519f91b7e3SAndre Fischer return 0; 3529f91b7e3SAndre Fischer } 3539f91b7e3SAndre Fischer else 3549f91b7e3SAndre Fischer { 3559f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: UpgradeCode values are identical\n"); 3569f91b7e3SAndre Fischer return 1; 3579f91b7e3SAndre Fischer } 3589f91b7e3SAndre Fischer} 3599f91b7e3SAndre Fischer 3609f91b7e3SAndre Fischer 3619f91b7e3SAndre Fischer 3629f91b7e3SAndre Fischer 3639f91b7e3SAndre Fischer=head2 CheckProductCode($source_msi, $target_msi) 3649f91b7e3SAndre Fischer 3659f91b7e3SAndre Fischer The 'ProductCode' values in the 'Property' tables remain the same. 3669f91b7e3SAndre Fischer 3679f91b7e3SAndre Fischer=cut 3689f91b7e3SAndre Fischersub CheckProductCode($$) 3699f91b7e3SAndre Fischer{ 3709f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 3719f91b7e3SAndre Fischer 3729f91b7e3SAndre Fischer my $source_product_code = $source_msi->GetTable("Property")->GetValue("Property", "ProductCode", "Value"); 3739f91b7e3SAndre Fischer my $target_product_code = $target_msi->GetTable("Property")->GetValue("Property", "ProductCode", "Value"); 3749f91b7e3SAndre Fischer 3759f91b7e3SAndre Fischer if ($source_product_code ne $target_product_code) 3769f91b7e3SAndre Fischer { 3779f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: The ProductCode properties have to remain the same but are\n"); 3789f91b7e3SAndre Fischer $installer::logger::Info->printf(" '%s' and '%s'\n", 3799f91b7e3SAndre Fischer $source_product_code, 3809f91b7e3SAndre Fischer $target_product_code); 3819f91b7e3SAndre Fischer return 0; 3829f91b7e3SAndre Fischer } 3839f91b7e3SAndre Fischer else 3849f91b7e3SAndre Fischer { 3859f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: ProductCode properties differ\n"); 3869f91b7e3SAndre Fischer return 1; 3879f91b7e3SAndre Fischer } 3889f91b7e3SAndre Fischer} 3899f91b7e3SAndre Fischer 3909f91b7e3SAndre Fischer 3919f91b7e3SAndre Fischer 3929f91b7e3SAndre Fischer 3939f91b7e3SAndre Fischer=head2 CheckBuildIdCode($source_msi, $target_msi) 3949f91b7e3SAndre Fischer 3959f91b7e3SAndre Fischer The 'PRODUCTBUILDID' values in the 'Property' tables (not the AOO build ids) differ and the 3969f91b7e3SAndre Fischer target value is higher than the source value. 3979f91b7e3SAndre Fischer 3989f91b7e3SAndre Fischer=cut 3999f91b7e3SAndre Fischersub CheckBuildIdCode($$) 4009f91b7e3SAndre Fischer{ 4019f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 4029f91b7e3SAndre Fischer 4039f91b7e3SAndre Fischer my $source_build_id = $source_msi->GetTable("Property")->GetValue("Property", "PRODUCTBUILDID", "Value"); 4049f91b7e3SAndre Fischer my $target_build_id = $target_msi->GetTable("Property")->GetValue("Property", "PRODUCTBUILDID", "Value"); 4059f91b7e3SAndre Fischer 4069f91b7e3SAndre Fischer if ($source_build_id >= $target_build_id) 4079f91b7e3SAndre Fischer { 4089f91b7e3SAndre Fischer $installer::logger::Info->printf( 4099f91b7e3SAndre Fischer "Error: The PRODUCTBUILDID properties have to increase but are '%s' and '%s'\n", 4109f91b7e3SAndre Fischer $source_build_id, 4119f91b7e3SAndre Fischer $target_build_id); 4129f91b7e3SAndre Fischer return 0; 4139f91b7e3SAndre Fischer } 4149f91b7e3SAndre Fischer else 4159f91b7e3SAndre Fischer { 4169f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: source build id is lower than target build id\n"); 4179f91b7e3SAndre Fischer return 1; 4189f91b7e3SAndre Fischer } 4199f91b7e3SAndre Fischer} 4209f91b7e3SAndre Fischer 4219f91b7e3SAndre Fischer 4229f91b7e3SAndre Fischer 4239f91b7e3SAndre Fischer 4249f91b7e3SAndre Fischersub CheckProductName ($$) 4259f91b7e3SAndre Fischer{ 4269f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 4279f91b7e3SAndre Fischer 4289f91b7e3SAndre Fischer my $source_product_name = $source_msi->GetTable("Property")->GetValue("Property", "DEFINEDPRODUCT", "Value"); 4299f91b7e3SAndre Fischer my $target_product_name = $target_msi->GetTable("Property")->GetValue("Property", "DEFINEDPRODUCT", "Value"); 4309f91b7e3SAndre Fischer 4319f91b7e3SAndre Fischer if ($source_product_name ne $target_product_name) 4329f91b7e3SAndre Fischer { 4339f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: product names of are not identical:\n"); 4349f91b7e3SAndre Fischer $installer::logger::Info->printf(" %s != %s\n", $source_product_name, $target_product_name); 4359f91b7e3SAndre Fischer return 0; 4369f91b7e3SAndre Fischer } 4379f91b7e3SAndre Fischer else 4389f91b7e3SAndre Fischer { 4399f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: product names are identical\n"); 4409f91b7e3SAndre Fischer return 1; 4419f91b7e3SAndre Fischer } 4429f91b7e3SAndre Fischer} 4439f91b7e3SAndre Fischer 4449f91b7e3SAndre Fischer 4459f91b7e3SAndre Fischer 4469f91b7e3SAndre Fischer 4479f91b7e3SAndre Fischer=head2 CheckRemovedFiles($source_msi, $target_msi) 4489f91b7e3SAndre Fischer 4499f91b7e3SAndre Fischer Files and components must not be deleted. 4509f91b7e3SAndre Fischer 4519f91b7e3SAndre Fischer=cut 4529f91b7e3SAndre Fischersub CheckRemovedFiles($$) 4539f91b7e3SAndre Fischer{ 4549f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 4559f91b7e3SAndre Fischer 4569f91b7e3SAndre Fischer # Get the 'File' tables. 4579f91b7e3SAndre Fischer my $source_file_table = $source_msi->GetTable("File"); 4589f91b7e3SAndre Fischer my $target_file_table = $target_msi->GetTable("File"); 4599f91b7e3SAndre Fischer 4609f91b7e3SAndre Fischer # Create data structures for fast lookup. 4619f91b7e3SAndre Fischer my @source_files = map {$_->GetValue("File")} @{$source_file_table->GetAllRows()}; 4629f91b7e3SAndre Fischer my %target_file_map = map {$_->GetValue("File") => $_} @{$target_file_table->GetAllRows()}; 4639f91b7e3SAndre Fischer 4649f91b7e3SAndre Fischer # Search for removed files (files in source that are missing from target). 4659f91b7e3SAndre Fischer my $removed_file_count = 0; 4669f91b7e3SAndre Fischer foreach my $uniquename (@source_files) 4679f91b7e3SAndre Fischer { 4689f91b7e3SAndre Fischer if ( ! defined $target_file_map{$uniquename}) 4699f91b7e3SAndre Fischer { 4709f91b7e3SAndre Fischer ++$removed_file_count; 4719f91b7e3SAndre Fischer } 4729f91b7e3SAndre Fischer } 4739f91b7e3SAndre Fischer 4749f91b7e3SAndre Fischer if ($removed_file_count > 0) 4759f91b7e3SAndre Fischer { 4769f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: %d files have been removed\n", $removed_file_count); 4779f91b7e3SAndre Fischer return 0; 4789f91b7e3SAndre Fischer } 4799f91b7e3SAndre Fischer else 4809f91b7e3SAndre Fischer { 4819f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: no files have been removed\n"); 4829f91b7e3SAndre Fischer return 1; 4839f91b7e3SAndre Fischer } 4849f91b7e3SAndre Fischer} 4859f91b7e3SAndre Fischer 4869f91b7e3SAndre Fischer 4879f91b7e3SAndre Fischer 4889f91b7e3SAndre Fischer 4899f91b7e3SAndre Fischer=head2 CheckNewFiles($source_msi, $target_msi) 4909f91b7e3SAndre Fischer 4919f91b7e3SAndre Fischer New files have to be in new components. 4929f91b7e3SAndre Fischer 4939f91b7e3SAndre Fischer=cut 4949f91b7e3SAndre Fischersub CheckNewFiles($$) 4959f91b7e3SAndre Fischer{ 4969f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 4979f91b7e3SAndre Fischer 4989f91b7e3SAndre Fischer # Get the 'File' tables. 4999f91b7e3SAndre Fischer my $source_file_table = $source_msi->GetTable("File"); 5009f91b7e3SAndre Fischer my $target_file_table = $target_msi->GetTable("File"); 5019f91b7e3SAndre Fischer 5029f91b7e3SAndre Fischer # Create data structures for fast lookup. 5039f91b7e3SAndre Fischer my %source_file_map = map {$_->GetValue("File") => $_} @{$source_file_table->GetAllRows()}; 5049f91b7e3SAndre Fischer my @target_files = map {$_->GetValue("File")} @{$target_file_table->GetAllRows()}; 5059f91b7e3SAndre Fischer 5069f91b7e3SAndre Fischer # Search for added files (files in target that where not in source). 5079f91b7e3SAndre Fischer my $added_file_count = 0; 5089f91b7e3SAndre Fischer foreach my $uniquename (@target_files) 5099f91b7e3SAndre Fischer { 5109f91b7e3SAndre Fischer if ( ! defined $source_file_map{$uniquename}) 5119f91b7e3SAndre Fischer { 5129f91b7e3SAndre Fischer ++$added_file_count; 5139f91b7e3SAndre Fischer } 5149f91b7e3SAndre Fischer } 5159f91b7e3SAndre Fischer 5169f91b7e3SAndre Fischer if ($added_file_count > 0) 5179f91b7e3SAndre Fischer { 5189f91b7e3SAndre Fischer $installer::logger::Info->printf("Warning: %d files have been added\n", $added_file_count); 5199f91b7e3SAndre Fischer 5209f91b7e3SAndre Fischer $installer::logger::Info->printf("Check for new files being part of new components is not yet implemented\n"); 5219f91b7e3SAndre Fischer 5229f91b7e3SAndre Fischer return 1; 5239f91b7e3SAndre Fischer } 5249f91b7e3SAndre Fischer else 5259f91b7e3SAndre Fischer { 5269f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: no files have been added\n"); 5279f91b7e3SAndre Fischer return 1; 5289f91b7e3SAndre Fischer } 5299f91b7e3SAndre Fischer} 5309f91b7e3SAndre Fischer 5319f91b7e3SAndre Fischer 5329f91b7e3SAndre Fischer 5339f91b7e3SAndre Fischer 5349f91b7e3SAndre Fischer=head2 CheckComponentSets($source_msi, $target_msi) 5359f91b7e3SAndre Fischer 5369f91b7e3SAndre Fischer Components must not be removed but can be added. 5379f91b7e3SAndre Fischer Features of added components have also to be new. 5389f91b7e3SAndre Fischer 5399f91b7e3SAndre Fischer=cut 5409f91b7e3SAndre Fischersub CheckComponentSets($$) 5419f91b7e3SAndre Fischer{ 5429f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 5439f91b7e3SAndre Fischer 5449f91b7e3SAndre Fischer # Get the 'Component' tables. 5459f91b7e3SAndre Fischer my $source_component_table = $source_msi->GetTable("Component"); 5469f91b7e3SAndre Fischer my $target_component_table = $target_msi->GetTable("Component"); 5479f91b7e3SAndre Fischer 5489f91b7e3SAndre Fischer # Create data structures for fast lookup. 5499f91b7e3SAndre Fischer my %source_component_map = map {$_->GetValue("Component") => $_} @{$source_component_table->GetAllRows()}; 5509f91b7e3SAndre Fischer my %target_component_map = map {$_->GetValue("Component") => $_} @{$target_component_table->GetAllRows()}; 5519f91b7e3SAndre Fischer 5529f91b7e3SAndre Fischer # Check that no component has been removed. 5539f91b7e3SAndre Fischer my @removed_components = (); 5549f91b7e3SAndre Fischer foreach my $componentname (keys %source_component_map) 5559f91b7e3SAndre Fischer { 5569f91b7e3SAndre Fischer if ( ! defined $target_component_map{$componentname}) 5579f91b7e3SAndre Fischer { 5589f91b7e3SAndre Fischer push @removed_components, $componentname; 5599f91b7e3SAndre Fischer } 5609f91b7e3SAndre Fischer } 5619f91b7e3SAndre Fischer if (scalar @removed_components > 0) 5629f91b7e3SAndre Fischer { 5639f91b7e3SAndre Fischer # There are removed components. 5649f91b7e3SAndre Fischer 5659f91b7e3SAndre Fischer # Check if any of them is not a registry component. 5669f91b7e3SAndre Fischer my $is_file_component_removed = 0; 5679f91b7e3SAndre Fischer foreach my $componentname (@removed_components) 5689f91b7e3SAndre Fischer { 5699f91b7e3SAndre Fischer if ($componentname !~ /^registry/) 5709f91b7e3SAndre Fischer { 5719f91b7e3SAndre Fischer $is_file_component_removed = 1; 5729f91b7e3SAndre Fischer } 5739f91b7e3SAndre Fischer } 5749f91b7e3SAndre Fischer if ($is_file_component_removed) 5759f91b7e3SAndre Fischer { 5769f91b7e3SAndre Fischer $installer::logger::Info->printf( 5779f91b7e3SAndre Fischer "Error: %d components have been removed, some of them are file components:\n", 5789f91b7e3SAndre Fischer scalar @removed_components); 5799f91b7e3SAndre Fischer $installer::logger::Info->printf(" %s\n", join(", ", @removed_components)); 5809f91b7e3SAndre Fischer return 0; 5819f91b7e3SAndre Fischer } 5829f91b7e3SAndre Fischer else 5839f91b7e3SAndre Fischer { 5849f91b7e3SAndre Fischer $installer::logger::Info->printf( 5859f91b7e3SAndre Fischer "Error: %d components have been removed, all of them are registry components:\n", 5869f91b7e3SAndre Fischer scalar @removed_components); 5879f91b7e3SAndre Fischer return 0; 5889f91b7e3SAndre Fischer } 5899f91b7e3SAndre Fischer } 5909f91b7e3SAndre Fischer 5919f91b7e3SAndre Fischer # Check that added components belong to new features. 5929f91b7e3SAndre Fischer my @added_components = (); 5939f91b7e3SAndre Fischer foreach my $componentname (keys %target_component_map) 5949f91b7e3SAndre Fischer { 5959f91b7e3SAndre Fischer if ( ! defined $source_component_map{$componentname}) 5969f91b7e3SAndre Fischer { 5979f91b7e3SAndre Fischer push @added_components, $componentname; 5989f91b7e3SAndre Fischer } 5999f91b7e3SAndre Fischer } 6009f91b7e3SAndre Fischer 6019f91b7e3SAndre Fischer if (scalar @added_components > 0) 6029f91b7e3SAndre Fischer { 6039f91b7e3SAndre Fischer # Check if any of them is not a registry component. 6049f91b7e3SAndre Fischer my $is_file_component_removed = 0; 6059f91b7e3SAndre Fischer foreach my $componentname (@removed_components) 6069f91b7e3SAndre Fischer { 6079f91b7e3SAndre Fischer if ($componentname !~ /^registry/) 6089f91b7e3SAndre Fischer { 6099f91b7e3SAndre Fischer $is_file_component_removed = 1; 6109f91b7e3SAndre Fischer } 6119f91b7e3SAndre Fischer } 6129f91b7e3SAndre Fischer 6139f91b7e3SAndre Fischer if ($is_file_component_removed) 6149f91b7e3SAndre Fischer { 6159f91b7e3SAndre Fischer $installer::logger::Info->printf( 6169f91b7e3SAndre Fischer "Warning: %d components have been addded\n", 6179f91b7e3SAndre Fischer scalar @added_components); 6189f91b7e3SAndre Fischer $installer::logger::Info->printf( 6199f91b7e3SAndre Fischer "Test for new components belonging to new features has not yet been implemented\n"); 6209f91b7e3SAndre Fischer return 0; 6219f91b7e3SAndre Fischer } 6229f91b7e3SAndre Fischer else 6239f91b7e3SAndre Fischer { 6249f91b7e3SAndre Fischer $installer::logger::Info->printf( 6259f91b7e3SAndre Fischer "Warning: %d components have been addded, all of them registry components\n", 6269f91b7e3SAndre Fischer scalar @added_components); 6279f91b7e3SAndre Fischer } 6289f91b7e3SAndre Fischer } 6299f91b7e3SAndre Fischer 6309f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: component sets in source and target are compatible\n"); 6319f91b7e3SAndre Fischer return 1; 6329f91b7e3SAndre Fischer} 6339f91b7e3SAndre Fischer 6349f91b7e3SAndre Fischer 6359f91b7e3SAndre Fischer 6369f91b7e3SAndre Fischer 6379f91b7e3SAndre Fischer=head2 CheckComponent($source_msi, $target_msi) 6389f91b7e3SAndre Fischer 6399f91b7e3SAndre Fischer In the 'Component' table the 'ComponentId' and 'Component' values 6409f91b7e3SAndre Fischer for corresponding componts in the source and target release have 6419f91b7e3SAndre Fischer to be identical. 6429f91b7e3SAndre Fischer 6439f91b7e3SAndre Fischer=cut 6449f91b7e3SAndre Fischersub CheckComponentValues($$$) 6459f91b7e3SAndre Fischer{ 6469f91b7e3SAndre Fischer my ($source_msi, $target_msi, $variables) = @_; 6479f91b7e3SAndre Fischer 6489f91b7e3SAndre Fischer # Get the 'Component' tables. 6499f91b7e3SAndre Fischer my $source_component_table = $source_msi->GetTable("Component"); 6509f91b7e3SAndre Fischer my $target_component_table = $target_msi->GetTable("Component"); 6519f91b7e3SAndre Fischer 6529f91b7e3SAndre Fischer # Create data structures for fast lookup. 6539f91b7e3SAndre Fischer my %source_component_map = map {$_->GetValue("Component") => $_} @{$source_component_table->GetAllRows()}; 6549f91b7e3SAndre Fischer my %target_component_map = map {$_->GetValue("Component") => $_} @{$target_component_table->GetAllRows()}; 6559f91b7e3SAndre Fischer 6569f91b7e3SAndre Fischer my @differences = (); 6579f91b7e3SAndre Fischer my $comparison_count = 0; 6589f91b7e3SAndre Fischer while (my ($componentname, $source_component_row) = each %source_component_map) 6599f91b7e3SAndre Fischer { 6609f91b7e3SAndre Fischer my $target_component_row = $target_component_map{$componentname}; 6619f91b7e3SAndre Fischer if (defined $target_component_row) 6629f91b7e3SAndre Fischer { 6639f91b7e3SAndre Fischer ++$comparison_count; 6649f91b7e3SAndre Fischer if ($source_component_row->GetValue("ComponentId") ne $target_component_row->GetValue("ComponentId")) 6659f91b7e3SAndre Fischer { 6669f91b7e3SAndre Fischer push @differences, [ 6679f91b7e3SAndre Fischer $componentname, 6689f91b7e3SAndre Fischer $source_component_row->GetValue("ComponentId"), 6699f91b7e3SAndre Fischer $target_component_row->GetValue("ComponentId"), 6709f91b7e3SAndre Fischer $target_component_row->GetValue("Component"), 6719f91b7e3SAndre Fischer ]; 6729f91b7e3SAndre Fischer } 6739f91b7e3SAndre Fischer } 6749f91b7e3SAndre Fischer } 6759f91b7e3SAndre Fischer 6769f91b7e3SAndre Fischer if (scalar @differences > 0) 6779f91b7e3SAndre Fischer { 6789f91b7e3SAndre Fischer $installer::logger::Info->printf( 6799f91b7e3SAndre Fischer "Error: there are %d components with different 'ComponentId' values after %d comparisons.\n", 6809f91b7e3SAndre Fischer scalar @differences, 6819f91b7e3SAndre Fischer $comparison_count); 6829f91b7e3SAndre Fischer foreach my $item (@differences) 6839f91b7e3SAndre Fischer { 6849f91b7e3SAndre Fischer $installer::logger::Info->printf("%s %s\n", $item->[1], $item->[2]); 6859f91b7e3SAndre Fischer } 6869f91b7e3SAndre Fischer return 0; 6879f91b7e3SAndre Fischer } 6889f91b7e3SAndre Fischer else 6899f91b7e3SAndre Fischer { 6909f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: components in source and target are identical\n"); 6919f91b7e3SAndre Fischer return 1; 6929f91b7e3SAndre Fischer } 6939f91b7e3SAndre Fischer} 6949f91b7e3SAndre Fischer 6959f91b7e3SAndre Fischer 6969f91b7e3SAndre Fischer 6979f91b7e3SAndre Fischer 6989f91b7e3SAndre Fischer=head2 CheckFileSequence($source_msi, $target_msi) 6999f91b7e3SAndre Fischer 7009f91b7e3SAndre Fischer In the 'File' table the 'Sequence' numbers for corresponding files has to be identical. 7019f91b7e3SAndre Fischer 7029f91b7e3SAndre Fischer=cut 7039f91b7e3SAndre Fischersub CheckFileSequence($$) 7049f91b7e3SAndre Fischer{ 7059f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 7069f91b7e3SAndre Fischer 7079f91b7e3SAndre Fischer # Get the 'File' tables. 7089f91b7e3SAndre Fischer my $source_file_table = $source_msi->GetTable("File"); 7099f91b7e3SAndre Fischer my $target_file_table = $target_msi->GetTable("File"); 7109f91b7e3SAndre Fischer 7119f91b7e3SAndre Fischer # Create temporary data structures for fast access. 7129f91b7e3SAndre Fischer my %source_file_map = map {$_->GetValue("File") => $_} @{$source_file_table->GetAllRows()}; 7139f91b7e3SAndre Fischer my %target_file_map = map {$_->GetValue("File") => $_} @{$target_file_table->GetAllRows()}; 7149f91b7e3SAndre Fischer 7159f91b7e3SAndre Fischer # Search files with mismatching sequence numbers. 7169f91b7e3SAndre Fischer my @mismatching_files = (); 7179f91b7e3SAndre Fischer while (my ($uniquename,$source_file_row) = each %source_file_map) 7189f91b7e3SAndre Fischer { 7199f91b7e3SAndre Fischer my $target_file_row = $target_file_map{$uniquename}; 7209f91b7e3SAndre Fischer if (defined $target_file_row) 7219f91b7e3SAndre Fischer { 7229f91b7e3SAndre Fischer if ($source_file_row->GetValue('Sequence') ne $target_file_row->GetValue('Sequence')) 7239f91b7e3SAndre Fischer { 7249f91b7e3SAndre Fischer push @mismatching_files, [ 7259f91b7e3SAndre Fischer $uniquename, 7269f91b7e3SAndre Fischer $source_file_row, 7279f91b7e3SAndre Fischer $target_file_row 7289f91b7e3SAndre Fischer ]; 7299f91b7e3SAndre Fischer } 7309f91b7e3SAndre Fischer } 7319f91b7e3SAndre Fischer } 7329f91b7e3SAndre Fischer 7339f91b7e3SAndre Fischer if (scalar @mismatching_files > 0) 7349f91b7e3SAndre Fischer { 7359f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: there are %d files with mismatching 'Sequence' numbers\n", 7369f91b7e3SAndre Fischer scalar @mismatching_files); 7379f91b7e3SAndre Fischer foreach my $item (@mismatching_files) 7389f91b7e3SAndre Fischer { 7399f91b7e3SAndre Fischer $installer::logger::Info->printf(" %s: %d != %d\n", 7409f91b7e3SAndre Fischer $item->[0], 7419f91b7e3SAndre Fischer $item->[1]->GetValue("Sequence"), 7429f91b7e3SAndre Fischer $item->[2]->GetValue("Sequence")); 7439f91b7e3SAndre Fischer } 7449f91b7e3SAndre Fischer return 0; 7459f91b7e3SAndre Fischer } 7469f91b7e3SAndre Fischer else 7479f91b7e3SAndre Fischer { 7489f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: all files have matching 'Sequence' numbers\n"); 7499f91b7e3SAndre Fischer return 1; 7509f91b7e3SAndre Fischer } 7519f91b7e3SAndre Fischer} 7529f91b7e3SAndre Fischer 7539f91b7e3SAndre Fischer 7549f91b7e3SAndre Fischer 7559f91b7e3SAndre Fischer 7569f91b7e3SAndre Fischer=head2 CheckFileSequenceUnique($source_msi, $target_msi) 7579f91b7e3SAndre Fischer 7589f91b7e3SAndre Fischer In the 'File' table the 'Sequence' values have to be unique. 7599f91b7e3SAndre Fischer 7609f91b7e3SAndre Fischer=cut 7619f91b7e3SAndre Fischersub CheckFileSequenceUnique($$) 7629f91b7e3SAndre Fischer{ 7639f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 7649f91b7e3SAndre Fischer 7659f91b7e3SAndre Fischer # Get the 'File' tables. 7669f91b7e3SAndre Fischer my $target_file_table = $target_msi->GetTable("File"); 7679f91b7e3SAndre Fischer 7689f91b7e3SAndre Fischer my %sequence_numbers = (); 7699f91b7e3SAndre Fischer my $collision_count = 0; 7709f91b7e3SAndre Fischer foreach my $row (@{$target_file_table->GetAllRows()}) 7719f91b7e3SAndre Fischer { 7729f91b7e3SAndre Fischer my $sequence_number = $row->GetValue("Sequence"); 7739f91b7e3SAndre Fischer if (defined $sequence_numbers{$sequence_number}) 7749f91b7e3SAndre Fischer { 7759f91b7e3SAndre Fischer ++$collision_count; 7769f91b7e3SAndre Fischer } 7779f91b7e3SAndre Fischer else 7789f91b7e3SAndre Fischer { 7799f91b7e3SAndre Fischer $sequence_numbers{$sequence_number} = 1; 7809f91b7e3SAndre Fischer } 7819f91b7e3SAndre Fischer } 7829f91b7e3SAndre Fischer 7839f91b7e3SAndre Fischer if ($collision_count > 0) 7849f91b7e3SAndre Fischer { 7859f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: there are %d collisions ofn the sequence numbers\n", 7869f91b7e3SAndre Fischer $collision_count); 7879f91b7e3SAndre Fischer return 0; 7889f91b7e3SAndre Fischer } 7899f91b7e3SAndre Fischer else 7909f91b7e3SAndre Fischer { 7919f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: sequence numbers are unique\n"); 7929f91b7e3SAndre Fischer return 1; 7939f91b7e3SAndre Fischer } 7949f91b7e3SAndre Fischer} 7959f91b7e3SAndre Fischer 7969f91b7e3SAndre Fischer 7979f91b7e3SAndre Fischer 7989f91b7e3SAndre Fischer 7999f91b7e3SAndre Fischer=head2 CheckFileSequenceHoles ($target_msi) 8009f91b7e3SAndre Fischer 8019f91b7e3SAndre Fischer Check the sequence numbers of the target msi if the n files use numbers 1..n or if there are holes. 8029f91b7e3SAndre Fischer Holes are reported as warnings. 8039f91b7e3SAndre Fischer 8049f91b7e3SAndre Fischer=cut 8059f91b7e3SAndre Fischersub CheckFileSequenceHoles ($$) 8069f91b7e3SAndre Fischer{ 8079f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 8089f91b7e3SAndre Fischer 8099f91b7e3SAndre Fischer my $target_file_table = $target_msi->GetTable("File"); 8109f91b7e3SAndre Fischer my %sequence_numbers = map {$_->GetValue("Sequence") => $_} @{$target_file_table->GetAllRows()}; 8119f91b7e3SAndre Fischer my @sorted_sequence_numbers = sort {$a <=> $b} keys %sequence_numbers; 8129f91b7e3SAndre Fischer my $expected_next_sequence_number = 1; 8139f91b7e3SAndre Fischer my @holes = (); 8149f91b7e3SAndre Fischer foreach my $sequence_number (@sorted_sequence_numbers) 8159f91b7e3SAndre Fischer { 8169f91b7e3SAndre Fischer if ($sequence_number != $expected_next_sequence_number) 8179f91b7e3SAndre Fischer { 8189f91b7e3SAndre Fischer push @holes, [$expected_next_sequence_number, $sequence_number-1]; 8199f91b7e3SAndre Fischer } 8209f91b7e3SAndre Fischer $expected_next_sequence_number = $sequence_number+1; 8219f91b7e3SAndre Fischer } 8229f91b7e3SAndre Fischer if (scalar @holes > 0) 8239f91b7e3SAndre Fischer { 8249f91b7e3SAndre Fischer $installer::logger::Info->printf("Warning: sequence numbers have %d holes\n"); 8259f91b7e3SAndre Fischer foreach my $hole (@holes) 8269f91b7e3SAndre Fischer { 8279f91b7e3SAndre Fischer if ($hole->[0] != $hole->[1]) 8289f91b7e3SAndre Fischer { 8299f91b7e3SAndre Fischer $installer::logger::Info->printf(" %d\n", $hole->[0]); 8309f91b7e3SAndre Fischer } 8319f91b7e3SAndre Fischer else 8329f91b7e3SAndre Fischer { 8339f91b7e3SAndre Fischer $installer::logger::Info->printf(" %d -> %d\n", $hole->[0], $hole->[1]); 8349f91b7e3SAndre Fischer } 8359f91b7e3SAndre Fischer } 8369f91b7e3SAndre Fischer } 8379f91b7e3SAndre Fischer else 8389f91b7e3SAndre Fischer { 8399f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: there are no holes in the sequence numbers\n"); 8409f91b7e3SAndre Fischer } 8419f91b7e3SAndre Fischer return 1; 8429f91b7e3SAndre Fischer} 8439f91b7e3SAndre Fischer 8449f91b7e3SAndre Fischer 8459f91b7e3SAndre Fischer 8469f91b7e3SAndre Fischer 8479f91b7e3SAndre Fischer=head2 CheckRegistryItems($source_msi, $target_msi) 8489f91b7e3SAndre Fischer 8499f91b7e3SAndre Fischer In the 'Registry' table the 'Component_' and 'Key' values must not 8509f91b7e3SAndre Fischer depend on the version number (beyond the unchanging major 8519f91b7e3SAndre Fischer version). 8529f91b7e3SAndre Fischer 8539f91b7e3SAndre Fischer 'Value' values must only depend on the major version number to 8549f91b7e3SAndre Fischer avoid duplicate entries in the start menu. 8559f91b7e3SAndre Fischer 8569f91b7e3SAndre Fischer Violations are reported as warnings for now. 8579f91b7e3SAndre Fischer 8589f91b7e3SAndre Fischer=cut 8599f91b7e3SAndre Fischersub CheckRegistryItems($$$) 8609f91b7e3SAndre Fischer{ 8619f91b7e3SAndre Fischer my ($source_msi, $target_msi, $product_name) = @_; 8629f91b7e3SAndre Fischer 8639f91b7e3SAndre Fischer # Get the registry tables. 8649f91b7e3SAndre Fischer my $source_registry_table = $source_msi->GetTable("Registry"); 8659f91b7e3SAndre Fischer my $target_registry_table = $target_msi->GetTable("Registry"); 8669f91b7e3SAndre Fischer 8679f91b7e3SAndre Fischer my $registry_index = $target_registry_table->GetColumnIndex("Registry"); 8689f91b7e3SAndre Fischer my $component_index = $target_registry_table->GetColumnIndex("Component_"); 8699f91b7e3SAndre Fischer 8709f91b7e3SAndre Fischer # Create temporary data structures for fast access. 8719f91b7e3SAndre Fischer my %source_registry_map = map {$_->GetValue($registry_index) => $_} @{$source_registry_table->GetAllRows()}; 8729f91b7e3SAndre Fischer my %target_registry_map = map {$_->GetValue($registry_index) => $_} @{$target_registry_table->GetAllRows()}; 8739f91b7e3SAndre Fischer 8749f91b7e3SAndre Fischer # Prepare version numbers to search. 8759f91b7e3SAndre Fischer my $source_version_number = $source_msi->{'version'}; 8769f91b7e3SAndre Fischer my $source_version_nodots = installer::patch::Version::ArrayToNoDotName( 8779f91b7e3SAndre Fischer installer::patch::Version::StringToNumberArray($source_version_number)); 8789f91b7e3SAndre Fischer my $source_component_pattern = lc($product_name).$source_version_nodots; 8799f91b7e3SAndre Fischer my $target_version_number = $target_msi->{'version'}; 8809f91b7e3SAndre Fischer my $target_version_nodots = installer::patch::Version::ArrayToNoDotName( 8819f91b7e3SAndre Fischer installer::patch::Version::StringToNumberArray($target_version_number)); 8829f91b7e3SAndre Fischer my $target_component_pattern = lc($product_name).$target_version_nodots; 8839f91b7e3SAndre Fischer 8849f91b7e3SAndre Fischer foreach my $source_row (values %source_registry_map) 8859f91b7e3SAndre Fischer { 8869f91b7e3SAndre Fischer my $target_row = $target_registry_map{$source_row->GetValue($registry_index)}; 8879f91b7e3SAndre Fischer if ( ! defined $target_row) 8889f91b7e3SAndre Fischer { 8899f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: sets of registry entries differs\n"); 8909f91b7e3SAndre Fischer return 1; 8919f91b7e3SAndre Fischer } 8929f91b7e3SAndre Fischer 8939f91b7e3SAndre Fischer my $source_component_name = $source_row->GetValue($component_index); 8949f91b7e3SAndre Fischer my $target_component_name = $source_row->GetValue($component_index); 8959f91b7e3SAndre Fischer 8969f91b7e3SAndre Fischer } 8979f91b7e3SAndre Fischer 8989f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: registry items are OK\n"); 8999f91b7e3SAndre Fischer return 1; 9009f91b7e3SAndre Fischer} 9019f91b7e3SAndre Fischer 9029f91b7e3SAndre Fischer 9039f91b7e3SAndre Fischer 9049f91b7e3SAndre Fischer 9059f91b7e3SAndre Fischer=head2 9069f91b7e3SAndre Fischer 9079f91b7e3SAndre Fischer Component->KeyPath must not change. (see component.pm/get_component_keypath) 9089f91b7e3SAndre Fischer 9099f91b7e3SAndre Fischer=cut 9109f91b7e3SAndre Fischersub CheckComponentKeyPath ($$) 9119f91b7e3SAndre Fischer{ 9129f91b7e3SAndre Fischer my ($source_msi, $target_msi) = @_; 9139f91b7e3SAndre Fischer 9149f91b7e3SAndre Fischer # Get the registry tables. 9159f91b7e3SAndre Fischer my $source_component_table = $source_msi->GetTable("Component"); 9169f91b7e3SAndre Fischer my $target_component_table = $target_msi->GetTable("Component"); 9179f91b7e3SAndre Fischer 9189f91b7e3SAndre Fischer # Create temporary data structures for fast access. 9199f91b7e3SAndre Fischer my %source_component_map = map {$_->GetValue("Component") => $_} @{$source_component_table->GetAllRows()}; 9209f91b7e3SAndre Fischer my %target_component_map = map {$_->GetValue("Component") => $_} @{$target_component_table->GetAllRows()}; 9219f91b7e3SAndre Fischer 9229f91b7e3SAndre Fischer my @mismatches = (); 9239f91b7e3SAndre Fischer while (my ($componentname, $source_component_row) = each %source_component_map) 9249f91b7e3SAndre Fischer { 9259f91b7e3SAndre Fischer my $target_component_row = $target_component_map{$componentname}; 9269f91b7e3SAndre Fischer if (defined $target_component_row) 9279f91b7e3SAndre Fischer { 9289f91b7e3SAndre Fischer my $source_keypath = $source_component_row->GetValue("KeyPath"); 9299f91b7e3SAndre Fischer my $target_keypath = $target_component_row->GetValue("KeyPath"); 9309f91b7e3SAndre Fischer if ($source_keypath ne $target_keypath) 9319f91b7e3SAndre Fischer { 9329f91b7e3SAndre Fischer push @mismatches, [$componentname, $source_keypath, $target_keypath]; 9339f91b7e3SAndre Fischer } 9349f91b7e3SAndre Fischer } 9359f91b7e3SAndre Fischer } 9369f91b7e3SAndre Fischer 9379f91b7e3SAndre Fischer if (scalar @mismatches > 0) 9389f91b7e3SAndre Fischer { 9399f91b7e3SAndre Fischer $installer::logger::Info->printf( 9409f91b7e3SAndre Fischer "Error: there are %d mismatches in the 'KeyPath' column of the 'Component' table\n", 9419f91b7e3SAndre Fischer scalar @mismatches); 9429f91b7e3SAndre Fischer 9439f91b7e3SAndre Fischer foreach my $item (@mismatches) 9449f91b7e3SAndre Fischer { 9459f91b7e3SAndre Fischer $installer::logger::Info->printf( 9469f91b7e3SAndre Fischer " %s: %s != %s\n", 9479f91b7e3SAndre Fischer $item->[0], 9489f91b7e3SAndre Fischer $item->[1], 9499f91b7e3SAndre Fischer $item->[2]); 9509f91b7e3SAndre Fischer } 9519f91b7e3SAndre Fischer 9529f91b7e3SAndre Fischer return 0; 9539f91b7e3SAndre Fischer } 9549f91b7e3SAndre Fischer else 9559f91b7e3SAndre Fischer { 9569f91b7e3SAndre Fischer $installer::logger::Info->printf( 9579f91b7e3SAndre Fischer "OK: no mismatches in the 'KeyPath' column of the 'Component' table\n"); 9589f91b7e3SAndre Fischer return 1; 9599f91b7e3SAndre Fischer } 9609f91b7e3SAndre Fischer} 9619f91b7e3SAndre Fischer 9629f91b7e3SAndre Fischer 9639f91b7e3SAndre Fischer 9649f91b7e3SAndre Fischer 9659f91b7e3SAndre Fischersub Check ($$$$) 9669f91b7e3SAndre Fischer{ 9679f91b7e3SAndre Fischer my ($source_msi, $target_msi, $variables, $product_name) = @_; 9689f91b7e3SAndre Fischer 9699f91b7e3SAndre Fischer $installer::logger::Info->printf("checking if source and target releases are compatable\n"); 9709f91b7e3SAndre Fischer $installer::logger::Info->increase_indentation(); 9719f91b7e3SAndre Fischer 9729f91b7e3SAndre Fischer my $result = 1; 9739f91b7e3SAndre Fischer 9749f91b7e3SAndre Fischer $result &&= CheckUpgradeCode($source_msi, $target_msi); 9759f91b7e3SAndre Fischer $result &&= CheckProductCode($source_msi, $target_msi); 9769f91b7e3SAndre Fischer $result &&= CheckBuildIdCode($source_msi, $target_msi); 9779f91b7e3SAndre Fischer $result &&= CheckProductName($source_msi, $target_msi); 9789f91b7e3SAndre Fischer $result &&= CheckRemovedFiles($source_msi, $target_msi); 9799f91b7e3SAndre Fischer $result &&= CheckNewFiles($source_msi, $target_msi); 9809f91b7e3SAndre Fischer $result &&= CheckComponentSets($source_msi, $target_msi); 9819f91b7e3SAndre Fischer $result &&= CheckComponentValues($source_msi, $target_msi, $variables); 9829f91b7e3SAndre Fischer $result &&= CheckFileSequence($source_msi, $target_msi); 9839f91b7e3SAndre Fischer $result &&= CheckFileSequenceUnique($source_msi, $target_msi); 9849f91b7e3SAndre Fischer $result &&= CheckFileSequenceHoles($source_msi, $target_msi); 9859f91b7e3SAndre Fischer $result &&= CheckRegistryItems($source_msi, $target_msi, $product_name); 9869f91b7e3SAndre Fischer $result &&= CheckComponentKeyPath($source_msi, $target_msi); 9879f91b7e3SAndre Fischer 9889f91b7e3SAndre Fischer $installer::logger::Info->decrease_indentation(); 9899f91b7e3SAndre Fischer 9909f91b7e3SAndre Fischer return $result; 9919f91b7e3SAndre Fischer} 9929f91b7e3SAndre Fischer 9939f91b7e3SAndre Fischer 9949f91b7e3SAndre Fischer 9959f91b7e3SAndre Fischer 9969f91b7e3SAndre Fischer=head2 FindPcpTemplate () 9979f91b7e3SAndre Fischer 9989f91b7e3SAndre Fischer The template.pcp file is part of the Windows SDK. 9999f91b7e3SAndre Fischer 10009f91b7e3SAndre Fischer=cut 10019f91b7e3SAndre Fischersub FindPcpTemplate () 10029f91b7e3SAndre Fischer{ 10039f91b7e3SAndre Fischer my $psdk_home = $ENV{'PSDK_HOME'}; 10049f91b7e3SAndre Fischer if ( ! defined $psdk_home) 10059f91b7e3SAndre Fischer { 10069f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: the PSDK_HOME environment variable is not set.\n"); 10079f91b7e3SAndre Fischer $installer::logger::Info->printf(" did you load the AOO build environment?\n"); 10089f91b7e3SAndre Fischer $installer::logger::Info->printf(" you may want to use the --with-psdk-home configure option\n"); 10099f91b7e3SAndre Fischer return undef; 10109f91b7e3SAndre Fischer } 10119f91b7e3SAndre Fischer if ( ! -d $psdk_home) 10129f91b7e3SAndre Fischer { 10139f91b7e3SAndre Fischer $installer::logger::Info->printf( 10149f91b7e3SAndre Fischer "Error: the PSDK_HOME environment variable does not point to a valid directory: %s\n", 10159f91b7e3SAndre Fischer $psdk_home); 10169f91b7e3SAndre Fischer return undef; 10179f91b7e3SAndre Fischer } 10189f91b7e3SAndre Fischer 10199f91b7e3SAndre Fischer my $schema_path = File::Spec->catfile($psdk_home, "Bin", "msitools", "Schemas", "MSI"); 10209f91b7e3SAndre Fischer if ( ! -d $schema_path) 10219f91b7e3SAndre Fischer { 10229f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: Can not locate the msi template folder in the Windows SDK\n"); 10239f91b7e3SAndre Fischer $installer::logger::Info->printf(" %s\n", $schema_path); 10249f91b7e3SAndre Fischer $installer::logger::Info->printf(" Is the Windows SDK properly installed?\n"); 10259f91b7e3SAndre Fischer return undef; 10269f91b7e3SAndre Fischer } 10279f91b7e3SAndre Fischer 10289f91b7e3SAndre Fischer my $schema_filename = File::Spec->catfile($schema_path, "template.pcp"); 10299f91b7e3SAndre Fischer if ( ! -f $schema_filename) 10309f91b7e3SAndre Fischer { 10319f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: Can not locate the pcp template at\n"); 10329f91b7e3SAndre Fischer $installer::logger::Info->printf(" %s\n", $schema_filename); 10339f91b7e3SAndre Fischer $installer::logger::Info->printf(" Is the Windows SDK properly installed?\n"); 10349f91b7e3SAndre Fischer return undef; 10359f91b7e3SAndre Fischer } 10369f91b7e3SAndre Fischer 10379f91b7e3SAndre Fischer return $schema_filename; 10389f91b7e3SAndre Fischer} 10399f91b7e3SAndre Fischer 10409f91b7e3SAndre Fischer 10419f91b7e3SAndre Fischer 10429f91b7e3SAndre Fischer 10439f91b7e3SAndre Fischersub SetupPcpPatchMetadataTable ($$$) 10449f91b7e3SAndre Fischer{ 10459f91b7e3SAndre Fischer my ($pcp, $source_msi, $target_msi) = @_; 10469f91b7e3SAndre Fischer 10479f91b7e3SAndre Fischer # Determine values for eg product name and source and new version. 10489f91b7e3SAndre Fischer my $source_version = $source_msi->{'version'}; 10499f91b7e3SAndre Fischer my $target_version = $target_msi->{'version'}; 10509f91b7e3SAndre Fischer 10519f91b7e3SAndre Fischer my $property_table = $target_msi->GetTable("Property"); 10529f91b7e3SAndre Fischer my $display_product_name = $property_table->GetValue("Property", "DEFINEDPRODUCT", "Value"); 10539f91b7e3SAndre Fischer 10549f91b7e3SAndre Fischer # Set table. 10559f91b7e3SAndre Fischer my $table = $pcp->GetTable("PatchMetadata"); 10569f91b7e3SAndre Fischer $table->SetRow( 10579f91b7e3SAndre Fischer "Company", "", 10589f91b7e3SAndre Fischer "*Property", "Description", 10599f91b7e3SAndre Fischer "Value", sprintf("Update of %s from %s to %s", $display_product_name, $source_version, $target_version) 10609f91b7e3SAndre Fischer ); 10619f91b7e3SAndre Fischer $table->SetRow( 10629f91b7e3SAndre Fischer "Company", "", 10639f91b7e3SAndre Fischer "*Property", "DisplayName", 10649f91b7e3SAndre Fischer "Value", sprintf("Update of %s from %s to %s", $display_product_name, $source_version, $target_version) 10659f91b7e3SAndre Fischer ); 10669f91b7e3SAndre Fischer $table->SetRow( 10679f91b7e3SAndre Fischer "Company", "", 10689f91b7e3SAndre Fischer "*Property", "ManufacturerName", 10699f91b7e3SAndre Fischer "Value", $property_table->GetValue("Property", "Manufacturer", "Value"), 10709f91b7e3SAndre Fischer ); 10719f91b7e3SAndre Fischer $table->SetRow( 10729f91b7e3SAndre Fischer "Company", "", 10739f91b7e3SAndre Fischer "*Property", "MoreInfoURL", 10749f91b7e3SAndre Fischer "Value", $property_table->GetValue("Property", "ARPURLINFOABOUT", "Value") 10759f91b7e3SAndre Fischer ); 10769f91b7e3SAndre Fischer $table->SetRow( 10779f91b7e3SAndre Fischer "Company", "", 10789f91b7e3SAndre Fischer "*Property", "TargetProductName", 10799f91b7e3SAndre Fischer "Value", $property_table->GetValue("Property", "ProductName", "Value") 10809f91b7e3SAndre Fischer ); 10819f91b7e3SAndre Fischer my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time); 10829f91b7e3SAndre Fischer 10839f91b7e3SAndre Fischer $table->SetRow( 10849f91b7e3SAndre Fischer "Company", "", 10859f91b7e3SAndre Fischer "*Property", "CreationTimeUTC", 10869f91b7e3SAndre Fischer "Value", sprintf("%d/%d/%d %d:%02d", $mon+1,$mday,$year+1900,$hour,$min) 10879f91b7e3SAndre Fischer ); 10889f91b7e3SAndre Fischer} 10899f91b7e3SAndre Fischer 10909f91b7e3SAndre Fischer 10919f91b7e3SAndre Fischer 10929f91b7e3SAndre Fischer 10939f91b7e3SAndre Fischersub SetupPropertiesTable ($$) 10949f91b7e3SAndre Fischer{ 10959f91b7e3SAndre Fischer my ($pcp, $msp_filename) = @_; 10969f91b7e3SAndre Fischer 10979f91b7e3SAndre Fischer my $table = $pcp->GetTable("Properties"); 10989f91b7e3SAndre Fischer 10999f91b7e3SAndre Fischer $table->SetRow( 11009f91b7e3SAndre Fischer "*Name", "PatchOutputPath", 11019f91b7e3SAndre Fischer "Value", installer::patch::Tools::ToWindowsPath($msp_filename) 11029f91b7e3SAndre Fischer ); 11039f91b7e3SAndre Fischer # Request at least Windows installer 2.0. 11049f91b7e3SAndre Fischer # Version 2.0 allows us to omit some values from ImageFamilies table. 11059f91b7e3SAndre Fischer $table->SetRow( 11069f91b7e3SAndre Fischer "*Name", "MinimumRequiredMsiVersion", 11079f91b7e3SAndre Fischer "Value", 200 11089f91b7e3SAndre Fischer ); 11099f91b7e3SAndre Fischer # Allow diffs for binary files. 11109f91b7e3SAndre Fischer $table->SetRow( 11119f91b7e3SAndre Fischer "*Name", "IncludeWholeFilesOnly", 11129f91b7e3SAndre Fischer "Value", 0 11139f91b7e3SAndre Fischer ); 11149f91b7e3SAndre Fischer 11159f91b7e3SAndre Fischer my $uuid = installer::windows::msiglobal::create_guid(); 11169f91b7e3SAndre Fischer my $uuid_string = "{" . $uuid . "}"; 11179f91b7e3SAndre Fischer $table->SetRow( 11189f91b7e3SAndre Fischer "*Name", "PatchGUID", 11199f91b7e3SAndre Fischer "Value", $uuid_string 11209f91b7e3SAndre Fischer ); 11219f91b7e3SAndre Fischer $installer::logger::Info->printf("created new PatchGUID %s\n", $uuid_string); 11229f91b7e3SAndre Fischer 11239f91b7e3SAndre Fischer # Prevent sequence table from being generated. 11249f91b7e3SAndre Fischer $table->SetRow( 11259f91b7e3SAndre Fischer "*Name", "SEQUENCE_DATA_GENERATION_DISABLED", 11269f91b7e3SAndre Fischer "Value", 1); 1127*d575d58fSAndre Fischer 1128*d575d58fSAndre Fischer # We don't provide file size and hash values. 1129*d575d58fSAndre Fischer # This value is set to make this fact explicit (0 should be the default). 1130*d575d58fSAndre Fischer $table->SetRow( 1131*d575d58fSAndre Fischer "*Name", "TrustMsi", 1132*d575d58fSAndre Fischer "Value", 0); 11339f91b7e3SAndre Fischer} 11349f91b7e3SAndre Fischer 11359f91b7e3SAndre Fischer 11369f91b7e3SAndre Fischer 11379f91b7e3SAndre Fischer 11389f91b7e3SAndre Fischersub SetupImageFamiliesTable ($) 11399f91b7e3SAndre Fischer{ 11409f91b7e3SAndre Fischer my ($pcp) = @_; 11419f91b7e3SAndre Fischer 11429f91b7e3SAndre Fischer $pcp->GetTable("ImageFamilies")->SetRow( 11439f91b7e3SAndre Fischer "Family", $ImageFamily, 11449f91b7e3SAndre Fischer "MediaSrcPropName", "",#"MNPSrcPropName", 11459f91b7e3SAndre Fischer "MediaDiskId", "", 11469f91b7e3SAndre Fischer "FileSequenceStart", "", 11479f91b7e3SAndre Fischer "DiskPrompt", "", 11489f91b7e3SAndre Fischer "VolumeLabel", ""); 11499f91b7e3SAndre Fischer} 11509f91b7e3SAndre Fischer 11519f91b7e3SAndre Fischer 11529f91b7e3SAndre Fischer 11539f91b7e3SAndre Fischer 11549f91b7e3SAndre Fischersub SetupUpgradedImagesTable ($$) 11559f91b7e3SAndre Fischer{ 11569f91b7e3SAndre Fischer my ($pcp, $target_msi_path) = @_; 11579f91b7e3SAndre Fischer 11589f91b7e3SAndre Fischer my $msi_path = installer::patch::Tools::ToWindowsPath($target_msi_path); 11599f91b7e3SAndre Fischer $pcp->GetTable("UpgradedImages")->SetRow( 11609f91b7e3SAndre Fischer "Upgraded", $TargetImageName, 11619f91b7e3SAndre Fischer "MsiPath", $msi_path, 11629f91b7e3SAndre Fischer "PatchMsiPath", "", 11639f91b7e3SAndre Fischer "SymbolPaths", "", 11649f91b7e3SAndre Fischer "Family", $ImageFamily); 11659f91b7e3SAndre Fischer} 11669f91b7e3SAndre Fischer 11679f91b7e3SAndre Fischer 11689f91b7e3SAndre Fischer 11699f91b7e3SAndre Fischer 11709f91b7e3SAndre Fischersub SetupTargetImagesTable ($$) 11719f91b7e3SAndre Fischer{ 11729f91b7e3SAndre Fischer my ($pcp, $source_msi_path) = @_; 11739f91b7e3SAndre Fischer 11749f91b7e3SAndre Fischer $pcp->GetTable("TargetImages")->SetRow( 11759f91b7e3SAndre Fischer "Target", $SourceImageName, 11769f91b7e3SAndre Fischer "MsiPath", installer::patch::Tools::ToWindowsPath($source_msi_path), 11779f91b7e3SAndre Fischer "SymbolPaths", "", 11789f91b7e3SAndre Fischer "Upgraded", $TargetImageName, 11799f91b7e3SAndre Fischer "Order", 1, 11809f91b7e3SAndre Fischer "ProductValidateFlags", "", 11819f91b7e3SAndre Fischer "IgnoreMissingSrcFiles", 0); 11829f91b7e3SAndre Fischer} 11839f91b7e3SAndre Fischer 11849f91b7e3SAndre Fischer 11859f91b7e3SAndre Fischer 11869f91b7e3SAndre Fischer 11879f91b7e3SAndre Fischersub SetAdditionalValues ($%) 11889f91b7e3SAndre Fischer{ 11899f91b7e3SAndre Fischer my ($pcp, %data) = @_; 11909f91b7e3SAndre Fischer 11919f91b7e3SAndre Fischer while (my ($key,$value) = each(%data)) 11929f91b7e3SAndre Fischer { 11939f91b7e3SAndre Fischer $key =~ /^([^\/]+)\/([^:]+):(.+)$/ 11949f91b7e3SAndre Fischer || die("invalid key format"); 11959f91b7e3SAndre Fischer my ($table_name, $key_column,$key_value) = ($1,$2,$3); 11969f91b7e3SAndre Fischer $value =~ /^([^:]+):(.*)$/ 11979f91b7e3SAndre Fischer || die("invalid value format"); 11989f91b7e3SAndre Fischer my ($value_column,$value_value) = ($1,$2); 11999f91b7e3SAndre Fischer 12009f91b7e3SAndre Fischer my $table = $pcp->GetTable($table_name); 12019f91b7e3SAndre Fischer $table->SetRow( 12029f91b7e3SAndre Fischer "*".$key_column, $key_value, 12039f91b7e3SAndre Fischer $value_column, $value_value); 12049f91b7e3SAndre Fischer } 12059f91b7e3SAndre Fischer} 12069f91b7e3SAndre Fischer 12079f91b7e3SAndre Fischer 12089f91b7e3SAndre Fischer 12099f91b7e3SAndre Fischer 12109f91b7e3SAndre Fischersub CreatePcp ($$$$$$%) 12119f91b7e3SAndre Fischer{ 12129f91b7e3SAndre Fischer my ($source_msi, 12139f91b7e3SAndre Fischer $target_msi, 12149f91b7e3SAndre Fischer $language, 12159f91b7e3SAndre Fischer $context, 12169f91b7e3SAndre Fischer $msp_path, 12179f91b7e3SAndre Fischer $pcp_schema_filename, 12189f91b7e3SAndre Fischer %additional_values) = @_; 12199f91b7e3SAndre Fischer 12209f91b7e3SAndre Fischer # Create filenames. 12219f91b7e3SAndre Fischer my $pcp_filename = File::Spec->catfile($msp_path, "openoffice.pcp"); 12229f91b7e3SAndre Fischer my $msp_filename = File::Spec->catfile($msp_path, "openoffice.msp"); 12239f91b7e3SAndre Fischer 12249f91b7e3SAndre Fischer # Setup msp path and filename. 12259f91b7e3SAndre Fischer unlink($pcp_filename) if -f $pcp_filename; 12269f91b7e3SAndre Fischer if ( ! File::Copy::copy($pcp_schema_filename, $pcp_filename)) 12279f91b7e3SAndre Fischer { 12289f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: could not create openoffice.pcp as copy of pcp schema\n"); 12299f91b7e3SAndre Fischer $installer::logger::Info->printf(" %s\n", $pcp_schema_filename); 12309f91b7e3SAndre Fischer $installer::logger::Info->printf(" %s\n", $pcp_filename); 12319f91b7e3SAndre Fischer return undef; 12329f91b7e3SAndre Fischer } 12339f91b7e3SAndre Fischer my $pcp = installer::patch::Msi->new( 12349f91b7e3SAndre Fischer $pcp_filename, 12359f91b7e3SAndre Fischer undef, 12369f91b7e3SAndre Fischer undef, 12379f91b7e3SAndre Fischer $language, 12389f91b7e3SAndre Fischer $context->{'product-name'}); 12399f91b7e3SAndre Fischer 12409f91b7e3SAndre Fischer # Store some values in the pcp for easy reference in the msp creation. 12419f91b7e3SAndre Fischer $pcp->{'msp_filename'} = $msp_filename; 12429f91b7e3SAndre Fischer 12439f91b7e3SAndre Fischer SetupPcpPatchMetadataTable($pcp, $source_msi, $target_msi); 12449f91b7e3SAndre Fischer SetupPropertiesTable($pcp, $msp_filename); 12459f91b7e3SAndre Fischer SetupImageFamiliesTable($pcp); 12469f91b7e3SAndre Fischer SetupUpgradedImagesTable($pcp, $target_msi->{'filename'}); 12479f91b7e3SAndre Fischer SetupTargetImagesTable($pcp, $source_msi->{'filename'}); 12489f91b7e3SAndre Fischer 12499f91b7e3SAndre Fischer SetAdditionalValues(%additional_values); 12509f91b7e3SAndre Fischer 12519f91b7e3SAndre Fischer $pcp->Commit(); 12529f91b7e3SAndre Fischer 12539f91b7e3SAndre Fischer # Remove the PatchSequence table to avoid MsiMsp error message: 12549f91b7e3SAndre Fischer # "Since MSI 3.0 will block installation of major upgrade patches with 12559f91b7e3SAndre Fischer # sequencing information, creation of such patches is blocked." 12569f91b7e3SAndre Fischer #$pcp->RemoveTable("PatchSequence"); 12579f91b7e3SAndre Fischer # TODO: alternatively add property SEQUENCE_DATA_GENERATION_DISABLED to pcp Properties table. 12589f91b7e3SAndre Fischer 12599f91b7e3SAndre Fischer 12609f91b7e3SAndre Fischer $installer::logger::Info->printf("created pcp file at\n"); 12619f91b7e3SAndre Fischer $installer::logger::Info->printf(" %s\n", $pcp->{'filename'}); 12629f91b7e3SAndre Fischer 12639f91b7e3SAndre Fischer return $pcp; 12649f91b7e3SAndre Fischer} 12659f91b7e3SAndre Fischer 12669f91b7e3SAndre Fischer 12679f91b7e3SAndre Fischer 12689f91b7e3SAndre Fischer 12699f91b7e3SAndre Fischersub ShowLog ($$$$) 12709f91b7e3SAndre Fischer{ 12719f91b7e3SAndre Fischer my ($log_path, $log_filename, $log_basename, $new_title) = @_; 12729f91b7e3SAndre Fischer 12739f91b7e3SAndre Fischer if ( -f $log_filename) 12749f91b7e3SAndre Fischer { 12759f91b7e3SAndre Fischer my $destination_path = File::Spec->catfile($log_path, $log_basename); 12769f91b7e3SAndre Fischer File::Path::make_path($destination_path) if ! -d $destination_path; 12779f91b7e3SAndre Fischer my $command = join(" ", 12789f91b7e3SAndre Fischer "wilogutl.exe", 12799f91b7e3SAndre Fischer "/q", 12809f91b7e3SAndre Fischer "/l", "'".installer::patch::Tools::ToWindowsPath($log_filename)."'", 12819f91b7e3SAndre Fischer "/o", "'".installer::patch::Tools::ToWindowsPath($destination_path)."'"); 12829f91b7e3SAndre Fischer printf("running command $command\n"); 12839f91b7e3SAndre Fischer my $response = qx($command); 12849f91b7e3SAndre Fischer printf("response is '%s'\n", $response); 12859f91b7e3SAndre Fischer my @candidates = glob($destination_path . "/Details*"); 12869f91b7e3SAndre Fischer foreach my $candidate (@candidates) 12879f91b7e3SAndre Fischer { 12889f91b7e3SAndre Fischer next unless -f $candidate; 12899f91b7e3SAndre Fischer my $new_name = $candidate; 12909f91b7e3SAndre Fischer $new_name =~ s/Details.*$/$log_basename.html/; 12919f91b7e3SAndre Fischer 12929f91b7e3SAndre Fischer # Rename the top-level html file and replace the title. 12939f91b7e3SAndre Fischer open my $in, "<", $candidate; 12949f91b7e3SAndre Fischer open my $out, ">", $new_name; 12959f91b7e3SAndre Fischer while (<$in>) 12969f91b7e3SAndre Fischer { 12979f91b7e3SAndre Fischer if (/^(.*\<title\>)([^<]+)(.*)$/) 12989f91b7e3SAndre Fischer { 12999f91b7e3SAndre Fischer print $out $1.$new_title.$3; 13009f91b7e3SAndre Fischer } 13019f91b7e3SAndre Fischer else 13029f91b7e3SAndre Fischer { 13039f91b7e3SAndre Fischer print $out $_; 13049f91b7e3SAndre Fischer } 13059f91b7e3SAndre Fischer } 13069f91b7e3SAndre Fischer close $in; 13079f91b7e3SAndre Fischer close $out; 13089f91b7e3SAndre Fischer 13099f91b7e3SAndre Fischer my $URL = $new_name; 13109f91b7e3SAndre Fischer $URL =~ s/\/c\//c|\//; 13119f91b7e3SAndre Fischer $URL =~ s/^(.):/$1|/; 13129f91b7e3SAndre Fischer $URL = "file:///". $URL; 13139f91b7e3SAndre Fischer $installer::logger::Info->printf("open %s in your browser to see the log messages\n", $URL); 13149f91b7e3SAndre Fischer } 13159f91b7e3SAndre Fischer } 13169f91b7e3SAndre Fischer else 13179f91b7e3SAndre Fischer { 13189f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: log file not found at %s\n", $log_filename); 13199f91b7e3SAndre Fischer } 13209f91b7e3SAndre Fischer} 13219f91b7e3SAndre Fischer 13229f91b7e3SAndre Fischer 13239f91b7e3SAndre Fischer 13249f91b7e3SAndre Fischer 13259f91b7e3SAndre Fischersub CreateMsp ($) 13269f91b7e3SAndre Fischer{ 13279f91b7e3SAndre Fischer my ($pcp) = @_; 13289f91b7e3SAndre Fischer 13299f91b7e3SAndre Fischer # Prepare log files. 13309f91b7e3SAndre Fischer my $log_path = File::Spec->catfile($pcp->{'path'}, "log"); 13319f91b7e3SAndre Fischer my $log_basename = "msp"; 13329f91b7e3SAndre Fischer my $log_filename = File::Spec->catfile($log_path, $log_basename.".log"); 13339f91b7e3SAndre Fischer my $performance_log_basename = "performance"; 13349f91b7e3SAndre Fischer my $performance_log_filename = File::Spec->catfile($log_path, $performance_log_basename.".log"); 13359f91b7e3SAndre Fischer File::Path::make_path($log_path) if ! -d $log_path; 13369f91b7e3SAndre Fischer unlink($log_filename) if -f $log_filename; 13379f91b7e3SAndre Fischer unlink($performance_log_filename) if -f $performance_log_filename; 13389f91b7e3SAndre Fischer 13399f91b7e3SAndre Fischer # Create the .msp patch file. 13409f91b7e3SAndre Fischer my $temporary_msimsp_path = File::Spec->catfile($pcp->{'path'}, "tmp"); 13419f91b7e3SAndre Fischer if ( ! -d $temporary_msimsp_path) 13429f91b7e3SAndre Fischer { 13439f91b7e3SAndre Fischer File::Path::make_path($temporary_msimsp_path) 13449f91b7e3SAndre Fischer || die ("can not create temporary path ".$temporary_msimsp_path); 13459f91b7e3SAndre Fischer } 13469f91b7e3SAndre Fischer $installer::logger::Info->printf("running msimsp.exe, that will take a while\n"); 13479f91b7e3SAndre Fischer my $command = join(" ", 13489f91b7e3SAndre Fischer "msimsp.exe", 13499f91b7e3SAndre Fischer "-s", "'".installer::patch::Tools::ToWindowsPath($pcp->{'filename'})."'", 13509f91b7e3SAndre Fischer "-p", "'".installer::patch::Tools::ToWindowsPath($pcp->{'msp_filename'})."'", 13519f91b7e3SAndre Fischer "-l", "'".installer::patch::Tools::ToWindowsPath($log_filename)."'", 13529f91b7e3SAndre Fischer "-f", "'".installer::patch::Tools::ToWindowsPath($temporary_msimsp_path)."'"); 13539f91b7e3SAndre Fischer# "-lp", MsiTools::ToEscapedWindowsPath($performance_log_filename), 13549f91b7e3SAndre Fischer $installer::logger::Info->printf("running command %s\n", $command); 13559f91b7e3SAndre Fischer my $response = qx($command); 13569f91b7e3SAndre Fischer $installer::logger::Info->printf("response of msimsp is %s\n", $response); 13579f91b7e3SAndre Fischer if ( ! -d $temporary_msimsp_path) 13589f91b7e3SAndre Fischer { 13599f91b7e3SAndre Fischer die("msimsp failed and deleted temporary path ".$temporary_msimsp_path); 13609f91b7e3SAndre Fischer } 13619f91b7e3SAndre Fischer 13629f91b7e3SAndre Fischer # Show the log file that was created by the msimsp.exe command. 13639f91b7e3SAndre Fischer ShowLog($log_path, $log_filename, $log_basename, "msp creation"); 13649f91b7e3SAndre Fischer ShowLog($log_path, $performance_log_filename, $performance_log_basename, "msp creation perf"); 13659f91b7e3SAndre Fischer} 13669f91b7e3SAndre Fischer 13679f91b7e3SAndre Fischer 13689f91b7e3SAndre Fischer 1369*d575d58fSAndre Fischer 1370*d575d58fSAndre Fischer=head CreatePatch($context, $variables) 1371*d575d58fSAndre Fischer 1372*d575d58fSAndre Fischer Create MSP patch files for all relevant languages. 1373*d575d58fSAndre Fischer The different steps are: 1374*d575d58fSAndre Fischer 1. Determine the set of languages for which both the source and target installation sets are present. 1375*d575d58fSAndre Fischer Per language: 1376*d575d58fSAndre Fischer 2. Unpack CAB files (for source and target). 1377*d575d58fSAndre Fischer 3. Check if source and target releases are compatible. 1378*d575d58fSAndre Fischer 4. Create the PCP driver file. 1379*d575d58fSAndre Fischer 5. Create the MSP patch file. 1380*d575d58fSAndre Fischer 1381*d575d58fSAndre Fischer=cut 13829f91b7e3SAndre Fischersub CreatePatch ($$) 13839f91b7e3SAndre Fischer{ 13849f91b7e3SAndre Fischer my ($context, $variables) = @_; 13859f91b7e3SAndre Fischer 13869f91b7e3SAndre Fischer $installer::logger::Info->printf("patch will update product %s from %s to %s\n", 13879f91b7e3SAndre Fischer $context->{'product-name'}, 13889f91b7e3SAndre Fischer $context->{'source-version'}, 13899f91b7e3SAndre Fischer $context->{'target-version'}); 13909f91b7e3SAndre Fischer 13919f91b7e3SAndre Fischer # Locate the Pcp schema file early on to report any errors before the lengthy operations that follow. 13929f91b7e3SAndre Fischer my $pcp_schema_filename = FindPcpTemplate(); 13939f91b7e3SAndre Fischer if ( ! defined $pcp_schema_filename) 13949f91b7e3SAndre Fischer { 13959f91b7e3SAndre Fischer exit(1); 13969f91b7e3SAndre Fischer } 13979f91b7e3SAndre Fischer 13989f91b7e3SAndre Fischer my $release_data = installer::patch::ReleasesList::Instance() 13999f91b7e3SAndre Fischer ->{$context->{'source-version'}} 14009f91b7e3SAndre Fischer ->{$context->{'package-format'}}; 14019f91b7e3SAndre Fischer 1402*d575d58fSAndre Fischer # 1. Determine the set of languages for which we can create patches. 14039f91b7e3SAndre Fischer my @requested_languages = GetLanguages(); 14049f91b7e3SAndre Fischer my @valid_languages = FindValidLanguages($context, $release_data, \@requested_languages); 14059f91b7e3SAndre Fischer $installer::logger::Info->printf("of the requested languages '%s' are valid: '%s'\n", 14069f91b7e3SAndre Fischer join("', '", @requested_languages), 14079f91b7e3SAndre Fischer join("', '", @valid_languages)); 14089f91b7e3SAndre Fischer foreach my $language (@valid_languages) 14099f91b7e3SAndre Fischer { 14109f91b7e3SAndre Fischer $installer::logger::Info->printf("processing language '%s'\n", $language); 14119f91b7e3SAndre Fischer $installer::logger::Info->increase_indentation(); 14129f91b7e3SAndre Fischer 1413*d575d58fSAndre Fischer # 2a. Provide .msi and .cab files and unpacke .cab for the source release. 14149f91b7e3SAndre Fischer $installer::logger::Info->printf("locating source package (%s)\n", $context->{'source-version'}); 14159f91b7e3SAndre Fischer $installer::logger::Info->increase_indentation(); 14169f91b7e3SAndre Fischer if ( ! installer::patch::InstallationSet::ProvideUnpackedCab( 14179f91b7e3SAndre Fischer $context->{'source-version'}, 14189f91b7e3SAndre Fischer 0, 14199f91b7e3SAndre Fischer $language, 14209f91b7e3SAndre Fischer "msi", 14219f91b7e3SAndre Fischer $context->{'product-name'})) 14229f91b7e3SAndre Fischer { 14239f91b7e3SAndre Fischer die "could not provide unpacked .cab file"; 14249f91b7e3SAndre Fischer } 14259f91b7e3SAndre Fischer my $source_msi = installer::patch::Msi->FindAndCreate( 14269f91b7e3SAndre Fischer $context->{'source-version'}, 14279f91b7e3SAndre Fischer 0, 14289f91b7e3SAndre Fischer $language, 14299f91b7e3SAndre Fischer $context->{'product-name'}); 14309f91b7e3SAndre Fischer die unless $source_msi->IsValid(); 14319f91b7e3SAndre Fischer 14329f91b7e3SAndre Fischer $installer::logger::Info->decrease_indentation(); 14339f91b7e3SAndre Fischer 1434*d575d58fSAndre Fischer # 2b. Provide .msi and .cab files and unpacke .cab for the target release. 14359f91b7e3SAndre Fischer $installer::logger::Info->printf("locating target package (%s)\n", $context->{'target-version'}); 14369f91b7e3SAndre Fischer $installer::logger::Info->increase_indentation(); 14379f91b7e3SAndre Fischer if ( ! installer::patch::InstallationSet::ProvideUnpackedCab( 14389f91b7e3SAndre Fischer $context->{'target-version'}, 14399f91b7e3SAndre Fischer 1, 14409f91b7e3SAndre Fischer $language, 14419f91b7e3SAndre Fischer "msi", 14429f91b7e3SAndre Fischer $context->{'product-name'})) 14439f91b7e3SAndre Fischer { 14449f91b7e3SAndre Fischer die; 14459f91b7e3SAndre Fischer } 14469f91b7e3SAndre Fischer my $target_msi = installer::patch::Msi->FindAndCreate( 14479f91b7e3SAndre Fischer $context->{'target-version'}, 14489f91b7e3SAndre Fischer 0, 14499f91b7e3SAndre Fischer $language, 14509f91b7e3SAndre Fischer $context->{'product-name'}); 14519f91b7e3SAndre Fischer die unless defined $target_msi; 14529f91b7e3SAndre Fischer die unless $target_msi->IsValid(); 14539f91b7e3SAndre Fischer $installer::logger::Info->decrease_indentation(); 14549f91b7e3SAndre Fischer 14559f91b7e3SAndre Fischer # Trigger reading of tables. 14569f91b7e3SAndre Fischer foreach my $table_name (("File", "Component", "Registry")) 14579f91b7e3SAndre Fischer { 14589f91b7e3SAndre Fischer $source_msi->GetTable($table_name); 14599f91b7e3SAndre Fischer $target_msi->GetTable($table_name); 14609f91b7e3SAndre Fischer $installer::logger::Info->printf("read %s table (source and target\n", $table_name); 14619f91b7e3SAndre Fischer } 14629f91b7e3SAndre Fischer 1463*d575d58fSAndre Fischer # 3. Check if the source and target msis fullfil all necessary requirements. 14649f91b7e3SAndre Fischer if ( ! Check($source_msi, $target_msi, $variables, $context->{'product-name'})) 14659f91b7e3SAndre Fischer { 14669f91b7e3SAndre Fischer $installer::logger::Info->printf("Error: Source and target releases are not compatible.\n"); 14679f91b7e3SAndre Fischer $installer::logger::Info->printf(" => Can not create patch.\n"); 14689f91b7e3SAndre Fischer $installer::logger::Info->printf(" Did you create the target installation set with 'release=t' ?\n"); 14699f91b7e3SAndre Fischer exit(1); 14709f91b7e3SAndre Fischer } 14719f91b7e3SAndre Fischer else 14729f91b7e3SAndre Fischer { 14739f91b7e3SAndre Fischer $installer::logger::Info->printf("OK: Source and target releases are compatible.\n"); 14749f91b7e3SAndre Fischer } 14759f91b7e3SAndre Fischer 14769f91b7e3SAndre Fischer # Provide the base path for creating .pcp and .mcp file. 14779f91b7e3SAndre Fischer my $msp_path = File::Spec->catfile( 14789f91b7e3SAndre Fischer $context->{'output-path'}, 14799f91b7e3SAndre Fischer $context->{'product-name'}, 14809f91b7e3SAndre Fischer "msp", 14819f91b7e3SAndre Fischer sprintf("%s_%s", 14829f91b7e3SAndre Fischer installer::patch::Version::ArrayToDirectoryName( 14839f91b7e3SAndre Fischer installer::patch::Version::StringToNumberArray( 14849f91b7e3SAndre Fischer $source_msi->{'version'})), 14859f91b7e3SAndre Fischer installer::patch::Version::ArrayToDirectoryName( 14869f91b7e3SAndre Fischer installer::patch::Version::StringToNumberArray( 14879f91b7e3SAndre Fischer $target_msi->{'version'}))), 14889f91b7e3SAndre Fischer $language 14899f91b7e3SAndre Fischer ); 14909f91b7e3SAndre Fischer File::Path::make_path($msp_path) unless -d $msp_path; 14919f91b7e3SAndre Fischer 1492*d575d58fSAndre Fischer # 4. Create the .pcp file that drives the msimsp.exe command. 14939f91b7e3SAndre Fischer my $pcp = CreatePcp( 14949f91b7e3SAndre Fischer $source_msi, 14959f91b7e3SAndre Fischer $target_msi, 14969f91b7e3SAndre Fischer $language, 14979f91b7e3SAndre Fischer $context, 14989f91b7e3SAndre Fischer $msp_path, 14999f91b7e3SAndre Fischer $pcp_schema_filename, 1500*d575d58fSAndre Fischer "Properties/Name:DontRemoveTempFolderWhenFinished" => "Value:1"); 15019f91b7e3SAndre Fischer 1502*d575d58fSAndre Fischer # 5. Finally create the msp. 15039f91b7e3SAndre Fischer CreateMsp($pcp); 15049f91b7e3SAndre Fischer 15059f91b7e3SAndre Fischer $installer::logger::Info->decrease_indentation(); 15069f91b7e3SAndre Fischer } 15079f91b7e3SAndre Fischer} 15089f91b7e3SAndre Fischer 15099f91b7e3SAndre Fischer 15109f91b7e3SAndre Fischer 1511*d575d58fSAndre Fischer=cut ApplyPatch ($context, $variables) 15129f91b7e3SAndre Fischer 1513*d575d58fSAndre Fischer This is for testing only. 1514*d575d58fSAndre Fischer The patch is applied and (extensive) log information is created and transformed into HTML format. 1515*d575d58fSAndre Fischer 1516*d575d58fSAndre Fischer=cut 15179f91b7e3SAndre Fischersub ApplyPatch ($$) 15189f91b7e3SAndre Fischer{ 15199f91b7e3SAndre Fischer my ($context, $variables) = @_; 15209f91b7e3SAndre Fischer 15219f91b7e3SAndre Fischer $installer::logger::Info->printf("will apply patches that update product %s from %s to %s\n", 15229f91b7e3SAndre Fischer $context->{'product-name'}, 15239f91b7e3SAndre Fischer $context->{'source-version'}, 15249f91b7e3SAndre Fischer $context->{'target-version'}); 15259f91b7e3SAndre Fischer my @languages = GetLanguages(); 15269f91b7e3SAndre Fischer 15279f91b7e3SAndre Fischer my $source_version_dirname = installer::patch::Version::ArrayToDirectoryName( 15289f91b7e3SAndre Fischer installer::patch::Version::StringToNumberArray( 15299f91b7e3SAndre Fischer $context->{'source-version'})); 15309f91b7e3SAndre Fischer my $target_version_dirname = installer::patch::Version::ArrayToDirectoryName( 15319f91b7e3SAndre Fischer installer::patch::Version::StringToNumberArray( 15329f91b7e3SAndre Fischer $context->{'target-version'})); 15339f91b7e3SAndre Fischer 15349f91b7e3SAndre Fischer foreach my $language (@languages) 15359f91b7e3SAndre Fischer { 15369f91b7e3SAndre Fischer my $msp_filename = File::Spec->catfile( 15379f91b7e3SAndre Fischer $context->{'output-path'}, 15389f91b7e3SAndre Fischer $context->{'product-name'}, 15399f91b7e3SAndre Fischer "msp", 15409f91b7e3SAndre Fischer $source_version_dirname . "_" . $target_version_dirname, 15419f91b7e3SAndre Fischer $language, 15429f91b7e3SAndre Fischer "openoffice.msp"); 15439f91b7e3SAndre Fischer if ( ! -f $msp_filename) 15449f91b7e3SAndre Fischer { 15459f91b7e3SAndre Fischer $installer::logger::Info->printf("%s does not point to a valid file\n", $msp_filename); 15469f91b7e3SAndre Fischer next; 15479f91b7e3SAndre Fischer } 15489f91b7e3SAndre Fischer 15499f91b7e3SAndre Fischer my $log_path = File::Spec->catfile(dirname($msp_filename), "log"); 15509f91b7e3SAndre Fischer my $log_basename = "apply-msp"; 15519f91b7e3SAndre Fischer my $log_filename = File::Spec->catfile($log_path, $log_basename.".log"); 15529f91b7e3SAndre Fischer 15539f91b7e3SAndre Fischer my $command = join(" ", 15549f91b7e3SAndre Fischer "msiexec.exe", 15559f91b7e3SAndre Fischer "/update", "'".installer::patch::Tools::ToWindowsPath($msp_filename)."'", 15569f91b7e3SAndre Fischer "/L*xv!", "'".installer::patch::Tools::ToWindowsPath($log_filename)."'", 15579f91b7e3SAndre Fischer "REINSTALL=ALL", 15589f91b7e3SAndre Fischer# "REINSTALLMODE=vomus", 15599f91b7e3SAndre Fischer "REINSTALLMODE=omus", 15609f91b7e3SAndre Fischer "MSIENFORCEUPGRADECOMPONENTRULES=1"); 15619f91b7e3SAndre Fischer 15629f91b7e3SAndre Fischer printf("executing command %s\n", $command); 15639f91b7e3SAndre Fischer my $response = qx($command); 15649f91b7e3SAndre Fischer Encode::from_to($response, "UTF16LE", "UTF8"); 15659f91b7e3SAndre Fischer printf("response was '%s'\n", $response); 15669f91b7e3SAndre Fischer 15679f91b7e3SAndre Fischer ShowLog($log_path, $log_filename, $log_basename, "msp application"); 15689f91b7e3SAndre Fischer } 15699f91b7e3SAndre Fischer} 15709f91b7e3SAndre Fischer 15719f91b7e3SAndre Fischer 15729f91b7e3SAndre Fischer 15739f91b7e3SAndre Fischer 1574*d575d58fSAndre Fischer=head2 DownloadFile ($url) 1575*d575d58fSAndre Fischer 1576*d575d58fSAndre Fischer A simpler version of InstallationSet::Download(). It is simple because it is used to 1577*d575d58fSAndre Fischer setup the $release_data structure that is used by InstallationSet::Download(). 1578*d575d58fSAndre Fischer 1579*d575d58fSAndre Fischer=cut 1580*d575d58fSAndre Fischersub DownloadFile ($) 1581*d575d58fSAndre Fischer{ 1582*d575d58fSAndre Fischer my ($url) = shift; 1583*d575d58fSAndre Fischer 1584*d575d58fSAndre Fischer my $agent = LWP::UserAgent->new(); 1585*d575d58fSAndre Fischer $agent->timeout(120); 1586*d575d58fSAndre Fischer $agent->show_progress(0); 1587*d575d58fSAndre Fischer 1588*d575d58fSAndre Fischer my $file_content = ""; 1589*d575d58fSAndre Fischer my $last_was_redirect = 0; 1590*d575d58fSAndre Fischer my $bytes_read = 0; 1591*d575d58fSAndre Fischer $agent->add_handler('response_redirect' 1592*d575d58fSAndre Fischer => sub{ 1593*d575d58fSAndre Fischer $last_was_redirect = 1; 1594*d575d58fSAndre Fischer return; 1595*d575d58fSAndre Fischer }); 1596*d575d58fSAndre Fischer $agent->add_handler('response_data' 1597*d575d58fSAndre Fischer => sub{ 1598*d575d58fSAndre Fischer if ($last_was_redirect) 1599*d575d58fSAndre Fischer { 1600*d575d58fSAndre Fischer $last_was_redirect = 0; 1601*d575d58fSAndre Fischer # Throw away the data we got so far. 1602*d575d58fSAndre Fischer $file_content = ""; 1603*d575d58fSAndre Fischer } 1604*d575d58fSAndre Fischer my($response,$agent,$h,$data)=@_; 1605*d575d58fSAndre Fischer $file_content .= $data; 1606*d575d58fSAndre Fischer }); 1607*d575d58fSAndre Fischer $agent->get($url); 1608*d575d58fSAndre Fischer 1609*d575d58fSAndre Fischer return $file_content; 1610*d575d58fSAndre Fischer} 1611*d575d58fSAndre Fischer 1612*d575d58fSAndre Fischer 1613*d575d58fSAndre Fischer 1614*d575d58fSAndre Fischer 1615*d575d58fSAndre Fischersub CreateReleaseItem ($$$) 1616*d575d58fSAndre Fischer{ 1617*d575d58fSAndre Fischer my ($language, $exe_filename, $msi) = @_; 1618*d575d58fSAndre Fischer 1619*d575d58fSAndre Fischer die "can not open installation set at ".$exe_filename unless -f $exe_filename; 1620*d575d58fSAndre Fischer 1621*d575d58fSAndre Fischer open my $in, "<", $exe_filename; 1622*d575d58fSAndre Fischer my $sha256_checksum = new Digest("SHA-256")->addfile($in)->hexdigest(); 1623*d575d58fSAndre Fischer close $in; 1624*d575d58fSAndre Fischer 1625*d575d58fSAndre Fischer my $filesize = -s $exe_filename; 1626*d575d58fSAndre Fischer 1627*d575d58fSAndre Fischer # Get the product code property from the msi and strip the enclosing braces. 1628*d575d58fSAndre Fischer my $product_code = $msi->GetTable("Property")->GetValue("Property", "ProductCode", "Value"); 1629*d575d58fSAndre Fischer $product_code =~ s/(^{|}$)//g; 1630*d575d58fSAndre Fischer my $upgrade_code = $msi->GetTable("Property")->GetValue("Property", "UpgradeCode", "Value"); 1631*d575d58fSAndre Fischer $upgrade_code =~ s/(^{|}$)//g; 1632*d575d58fSAndre Fischer my $build_id = $msi->GetTable("Property")->GetValue("Property", "PRODUCTBUILDID", "Value"); 1633*d575d58fSAndre Fischer 1634*d575d58fSAndre Fischer return { 1635*d575d58fSAndre Fischer 'language' => $language, 1636*d575d58fSAndre Fischer 'checksum-type' => "sha256", 1637*d575d58fSAndre Fischer 'checksum-value' => $sha256_checksum, 1638*d575d58fSAndre Fischer 'file-size' => $filesize, 1639*d575d58fSAndre Fischer 'product-code' => $product_code, 1640*d575d58fSAndre Fischer 'upgrade-code' => $upgrade_code, 1641*d575d58fSAndre Fischer 'build-id' => $build_id 1642*d575d58fSAndre Fischer }; 1643*d575d58fSAndre Fischer} 1644*d575d58fSAndre Fischer 1645*d575d58fSAndre Fischer 1646*d575d58fSAndre Fischer 1647*d575d58fSAndre Fischer 1648*d575d58fSAndre Fischersub GetReleaseItemForCurrentBuild ($$$) 1649*d575d58fSAndre Fischer{ 1650*d575d58fSAndre Fischer my ($context, $language, $exe_basename) = @_; 1651*d575d58fSAndre Fischer 1652*d575d58fSAndre Fischer # Target version is the current version. 1653*d575d58fSAndre Fischer # Search instsetoo_native for the installation set. 1654*d575d58fSAndre Fischer my $filename = File::Spec->catfile( 1655*d575d58fSAndre Fischer $context->{'output-path'}, 1656*d575d58fSAndre Fischer $context->{'product-name'}, 1657*d575d58fSAndre Fischer $context->{'package-format'}, 1658*d575d58fSAndre Fischer "install", 1659*d575d58fSAndre Fischer $language."_download", 1660*d575d58fSAndre Fischer $exe_basename); 1661*d575d58fSAndre Fischer 1662*d575d58fSAndre Fischer printf(" current : %s\n", $filename); 1663*d575d58fSAndre Fischer if ( ! -f $filename) 1664*d575d58fSAndre Fischer { 1665*d575d58fSAndre Fischer printf("ERROR: can not find %s\n", $filename); 1666*d575d58fSAndre Fischer return undef; 1667*d575d58fSAndre Fischer } 1668*d575d58fSAndre Fischer else 1669*d575d58fSAndre Fischer { 1670*d575d58fSAndre Fischer my $msi = installer::patch::Msi->FindAndCreate( 1671*d575d58fSAndre Fischer $context->{'target-version'}, 1672*d575d58fSAndre Fischer 1, 1673*d575d58fSAndre Fischer $language, 1674*d575d58fSAndre Fischer $context->{'product-name'}); 1675*d575d58fSAndre Fischer return CreateReleaseItem($language, $filename, $msi); 1676*d575d58fSAndre Fischer } 1677*d575d58fSAndre Fischer} 1678*d575d58fSAndre Fischer 1679*d575d58fSAndre Fischer 1680*d575d58fSAndre Fischer 1681*d575d58fSAndre Fischersub GetReleaseItemForOldBuild ($$$$) 1682*d575d58fSAndre Fischer{ 1683*d575d58fSAndre Fischer my ($context, $language, $exe_basename, $url_template) = @_; 1684*d575d58fSAndre Fischer 1685*d575d58fSAndre Fischer # Use ext_sources/ as local cache for archive.apache.org 1686*d575d58fSAndre Fischer # and search these for the installation set. 1687*d575d58fSAndre Fischer 1688*d575d58fSAndre Fischer my $version = $context->{'target-version'}; 1689*d575d58fSAndre Fischer my $package_format = $context->{'package-format'}; 1690*d575d58fSAndre Fischer my $releases_list = installer::patch::ReleasesList::Instance(); 1691*d575d58fSAndre Fischer 1692*d575d58fSAndre Fischer my $url = $url_template; 1693*d575d58fSAndre Fischer $url =~ s/%L/$language/g; 1694*d575d58fSAndre Fischer $releases_list->{$version}->{$package_format}->{$language}->{'URL'} = $url; 1695*d575d58fSAndre Fischer 1696*d575d58fSAndre Fischer if ( ! installer::patch::InstallationSet::ProvideUnpackedExe( 1697*d575d58fSAndre Fischer $version, 1698*d575d58fSAndre Fischer 0, 1699*d575d58fSAndre Fischer $language, 1700*d575d58fSAndre Fischer $package_format, 1701*d575d58fSAndre Fischer $context->{'product-name'})) 1702*d575d58fSAndre Fischer { 1703*d575d58fSAndre Fischer # Can not provide unpacked EXE. 1704*d575d58fSAndre Fischer return undef; 1705*d575d58fSAndre Fischer } 1706*d575d58fSAndre Fischer else 1707*d575d58fSAndre Fischer { 1708*d575d58fSAndre Fischer my $exe_filename = File::Spec->catfile( 1709*d575d58fSAndre Fischer $ENV{'TARFILE_LOCATION'}, 1710*d575d58fSAndre Fischer $exe_basename); 1711*d575d58fSAndre Fischer my $msi = installer::patch::Msi->FindAndCreate( 1712*d575d58fSAndre Fischer $version, 1713*d575d58fSAndre Fischer 0, 1714*d575d58fSAndre Fischer $language, 1715*d575d58fSAndre Fischer $context->{'product-name'}); 1716*d575d58fSAndre Fischer return CreateReleaseItem($language, $exe_filename, $msi); 1717*d575d58fSAndre Fischer } 1718*d575d58fSAndre Fischer} 1719*d575d58fSAndre Fischer 1720*d575d58fSAndre Fischer 1721*d575d58fSAndre Fischer 1722*d575d58fSAndre Fischer 1723*d575d58fSAndre Fischersub UpdateReleasesXML($$) 1724*d575d58fSAndre Fischer{ 1725*d575d58fSAndre Fischer my ($context, $variables) = @_; 1726*d575d58fSAndre Fischer 1727*d575d58fSAndre Fischer my $releases_list = installer::patch::ReleasesList::Instance(); 1728*d575d58fSAndre Fischer my $output_filename = File::Spec->catfile( 1729*d575d58fSAndre Fischer $context->{'output-path'}, 1730*d575d58fSAndre Fischer "misc", 1731*d575d58fSAndre Fischer "releases.xml"); 1732*d575d58fSAndre Fischer 1733*d575d58fSAndre Fischer my $target_version = $context->{'target-version'}; 1734*d575d58fSAndre Fischer my %version_hash = map {$_=>1} @{$releases_list->{'releases'}}; 1735*d575d58fSAndre Fischer my $item_hash = undef; 1736*d575d58fSAndre Fischer if ( ! defined $version_hash{$context->{'target-version'}}) 1737*d575d58fSAndre Fischer { 1738*d575d58fSAndre Fischer # Target version is not yet present. Add it and print message that asks caller to check order. 1739*d575d58fSAndre Fischer push @{$releases_list->{'releases'}}, $target_version; 1740*d575d58fSAndre Fischer printf("adding data for new version %s to list of released versions.\n", $target_version); 1741*d575d58fSAndre Fischer printf("please check order of releases in $output_filename\n"); 1742*d575d58fSAndre Fischer $item_hash = {}; 1743*d575d58fSAndre Fischer } 1744*d575d58fSAndre Fischer else 1745*d575d58fSAndre Fischer { 1746*d575d58fSAndre Fischer printf("adding data for existing version %s to releases.xml\n", $target_version); 1747*d575d58fSAndre Fischer $item_hash = $releases_list->{$target_version}->{$context->{'package-format'}}; 1748*d575d58fSAndre Fischer } 1749*d575d58fSAndre Fischer $releases_list->{$target_version} = {$context->{'package-format'} => $item_hash}; 1750*d575d58fSAndre Fischer 1751*d575d58fSAndre Fischer my @languages = GetLanguages(); 1752*d575d58fSAndre Fischer my %language_items = (); 1753*d575d58fSAndre Fischer foreach my $language (@languages) 1754*d575d58fSAndre Fischer { 1755*d575d58fSAndre Fischer # There are three different sources where to find the downloadable installation sets. 1756*d575d58fSAndre Fischer # 1. archive.apache.org for previously released versions. 1757*d575d58fSAndre Fischer # 2. A local cache or repository directory that conceptually is a local copy of archive.apache.org 1758*d575d58fSAndre Fischer # 3. The downloadable installation sets built in instsetoo_native/. 1759*d575d58fSAndre Fischer 1760*d575d58fSAndre Fischer my $exe_basename = sprintf( 1761*d575d58fSAndre Fischer "%s_%s_Win_x86_install_%s.exe", 1762*d575d58fSAndre Fischer $context->{'product-name'}, 1763*d575d58fSAndre Fischer $target_version, 1764*d575d58fSAndre Fischer $language); 1765*d575d58fSAndre Fischer my $url_template = sprintf( 1766*d575d58fSAndre Fischer "http://archive.apache.org/dist/openoffice/%s/binaries/%%L/%s_%s_Win_x86_install_%%L.exe", 1767*d575d58fSAndre Fischer $target_version, 1768*d575d58fSAndre Fischer $context->{'product-name'}, 1769*d575d58fSAndre Fischer $target_version); 1770*d575d58fSAndre Fischer 1771*d575d58fSAndre Fischer my $item = undef; 1772*d575d58fSAndre Fischer if ($target_version eq $variables->{PRODUCTVERSION}) 1773*d575d58fSAndre Fischer { 1774*d575d58fSAndre Fischer $item = GetReleaseItemForCurrentBuild($context, $language, $exe_basename); 1775*d575d58fSAndre Fischer } 1776*d575d58fSAndre Fischer else 1777*d575d58fSAndre Fischer { 1778*d575d58fSAndre Fischer $item = GetReleaseItemForOldBuild($context, $language, $exe_basename, $url_template); 1779*d575d58fSAndre Fischer } 1780*d575d58fSAndre Fischer 1781*d575d58fSAndre Fischer next unless defined $item; 1782*d575d58fSAndre Fischer 1783*d575d58fSAndre Fischer $language_items{$language} = $item; 1784*d575d58fSAndre Fischer $item_hash->{$language} = $item; 1785*d575d58fSAndre Fischer $item_hash->{'upgrade-code'} = $item->{'upgrade-code'}; 1786*d575d58fSAndre Fischer $item_hash->{'build-id'} = $item->{'build-id'}; 1787*d575d58fSAndre Fischer $item_hash->{'url-template'} = $url_template; 1788*d575d58fSAndre Fischer } 1789*d575d58fSAndre Fischer 1790*d575d58fSAndre Fischer my @valid_languages = sort keys %language_items; 1791*d575d58fSAndre Fischer $item_hash->{'languages'} = \@valid_languages; 1792*d575d58fSAndre Fischer 1793*d575d58fSAndre Fischer $releases_list->Write($output_filename); 1794*d575d58fSAndre Fischer 1795*d575d58fSAndre Fischer printf("\n\n"); 1796*d575d58fSAndre Fischer printf("please copy '%s' to main/instsetoo_native/data\n", $output_filename); 1797*d575d58fSAndre Fischer printf("and check in the modified file to the version control system\n"); 1798*d575d58fSAndre Fischer} 1799*d575d58fSAndre Fischer 1800*d575d58fSAndre Fischer 1801*d575d58fSAndre Fischer 1802*d575d58fSAndre Fischer 18039f91b7e3SAndre Fischersub main () 18049f91b7e3SAndre Fischer{ 18059f91b7e3SAndre Fischer installer::logger::SetupSimpleLogging(undef); 18069f91b7e3SAndre Fischer my $context = ProcessCommandline(); 1807*d575d58fSAndre Fischer die "ERROR: list file is not defined, please use --lst-file option" 1808*d575d58fSAndre Fischer unless defined $context->{'lst-file'}; 1809*d575d58fSAndre Fischer die "ERROR: product name is not defined, please use --product-name option" 1810*d575d58fSAndre Fischer unless defined $context->{'product-name'}; 1811*d575d58fSAndre Fischer 18129f91b7e3SAndre Fischer my ($variables, undef, undef) = installer::ziplist::read_openoffice_lst_file( 18139f91b7e3SAndre Fischer $context->{'lst-file'}, 18149f91b7e3SAndre Fischer $context->{'product-name'}, 18159f91b7e3SAndre Fischer undef); 18169f91b7e3SAndre Fischer DetermineVersions($context, $variables); 18179f91b7e3SAndre Fischer 18189f91b7e3SAndre Fischer if ($context->{'command'} eq "create") 18199f91b7e3SAndre Fischer { 18209f91b7e3SAndre Fischer CreatePatch($context, $variables); 18219f91b7e3SAndre Fischer } 18229f91b7e3SAndre Fischer elsif ($context->{'command'} eq "apply") 18239f91b7e3SAndre Fischer { 18249f91b7e3SAndre Fischer ApplyPatch($context, $variables); 18259f91b7e3SAndre Fischer } 1826*d575d58fSAndre Fischer elsif ($context->{'command'} eq "update-releases-xml") 1827*d575d58fSAndre Fischer { 1828*d575d58fSAndre Fischer UpdateReleasesXML($context, $variables); 1829*d575d58fSAndre Fischer } 18309f91b7e3SAndre Fischer} 18319f91b7e3SAndre Fischer 18329f91b7e3SAndre Fischer 18339f91b7e3SAndre Fischermain(); 1834