#************************************************************** # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # #************************************************************** package installer::windows::feature; use installer::existence; use installer::exiter; use installer::files; use installer::globals; use installer::sorter; use installer::worker; use installer::windows::idtglobal; use installer::windows::language; ############################################################## # Returning the gid for a feature. # Attention: Maximum length ############################################################## sub get_feature_gid { my ($onefeature) = @_; my $gid = ""; if ( $onefeature->{'gid'} ) { $gid = $onefeature->{'gid'}; } # Attention: Maximum feature length is 38! installer::windows::idtglobal::shorten_feature_gid(\$gid); return $gid } ############################################################## # Returning the gid of the parent. # Attention: Maximum length ############################################################## sub get_feature_parent { my ($onefeature) = @_; my $parentgid = ""; if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; } # The modules, hanging directly below the root, have to be root modules. # Only then it is possible to make the "real" root module invisible by # setting the display to "0". if ( $parentgid eq $installer::globals::rootmodulegid ) { $parentgid = ""; } # Attention: Maximum feature length is 38! installer::windows::idtglobal::shorten_feature_gid(\$parentgid); return $parentgid } ############################################################## # Returning the display for a feature. # 0: Feature is not shown # odd: subfeatures are shown # even: subfeatures are not shown ############################################################## sub get_feature_display { my ($onefeature) = @_; my $display; my $parentid = ""; if ( $onefeature->{'ParentID'} ) { $parentid = $onefeature->{'ParentID'}; } if ( $parentid eq "" ) { $display = "0"; # root module is not visible } elsif ( $onefeature->{'gid'} eq "gid_Module_Prg") # program module shows subfeatures { $display = "1"; # root module shows subfeatures } else { $display = "2"; # all other modules do not show subfeatures } # special case: Feature has flag "HIDDEN_ROOT" -> $display is 0 my $styles = ""; if ( $onefeature->{'Styles'} ) { $styles = $onefeature->{'Styles'}; } if ( $styles =~ /\bHIDDEN_ROOT\b/ ) { $display = "0"; } # Special handling for language modules. Only visible in multilingual installation set if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && ( ! $installer::globals::ismultilingual )) { $display = "0"; } # Special handling for c05office. No program module visible. if (( $onefeature->{'gid'} eq "gid_Module_Prg" ) && ( $installer::globals::product =~ /c05office/i )) { $display = "0"; } # making all feature invisible in Language packs! if ( $installer::globals::languagepack ) { $display = "0"; } return $display } ############################################################## # Returning the level for a feature. ############################################################## sub get_feature_level { my ($onefeature) = @_; my $level = "20"; # the default my $localdefault = ""; if ( $onefeature->{'Default'} ) { $localdefault = $onefeature->{'Default'}; } if ( $localdefault eq "NO" ) # explicitely set Default = "NO" { $level = "200"; # deselected in default installation, base is 100 if ( $installer::globals::patch ) { $level = "20"; } } # special handling for Java and Ada if ( $onefeature->{'Name'} ) { if ( $onefeature->{'Name'} =~ /java/i ) { $level = $level + 40; } } # if FeatureLevel is defined in scp, this will be used if ( $onefeature->{'FeatureLevel'} ) { $level = $onefeature->{'FeatureLevel'}; } return $level } ############################################################## # Returning the directory for a feature. ############################################################## sub get_feature_directory { my ($onefeature) = @_; my $directory; $directory = "INSTALLLOCATION"; return $directory } ############################################################## # Returning the directory for a feature. ############################################################## sub get_feature_attributes { my ($onefeature) = @_; my $attributes; # No advertising of features and no leaving on network. # Feature without parent must not have the "2" my $parentgid = ""; if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; } if (( $parentgid eq "" ) || ( $parentgid eq $installer::globals::rootmodulegid )) { $attributes = "8"; } else { $attributes = "10"; } return $attributes } ################################################################################# # Replacing one variable in one files ################################################################################# sub replace_one_variable { my ($translationfile, $variable, $searchstring) = @_; for ( my $i = 0; $i <= $#{$translationfile}; $i++ ) { ${$translationfile}[$i] =~ s/\%$searchstring/$variable/g; } } ################################################################################# # Replacing the variables in the feature names and descriptions ################################################################################# sub replace_variables { my ($translationfile, $variableshashref) = @_; foreach $key (keys %{$variableshashref}) { my $value = $variableshashref->{$key}; replace_one_variable($translationfile, $value, $key); } } ################################################################################# # Collecting the feature recursively. ################################################################################# sub collect_modules_recursive { my ($modulesref, $parentid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted) = @_; my @allchildren = (); my $childrenexist = 0; # Collecting children from Module $parentid my $modulegid; foreach $modulegid ( keys %{$directparent}) { if ( $directparent->{$modulegid} eq $parentid ) { my %childhash = ( "gid" => "$modulegid", "Sortkey" => "$directsortkey->{$modulegid}"); push(@allchildren, \%childhash); $childrenexist = 1; } } # Sorting children if ( $childrenexist ) { # Sort children installer::sorter::sort_array_of_hashes_numerically(\@allchildren, "Sortkey"); # Adding children to new array my $childhashref; foreach $childhashref ( @allchildren ) { my $gid = $childhashref->{'gid'}; # Saving all lines, that have this 'gid' my $unique; foreach $unique ( keys %{$directgid} ) { if ( $directgid->{$unique} eq $gid ) { push(@{$feature}, ${$modulesref}[$directaccess->{$unique}]); if ( $sorted->{$unique} == 1 ) { installer::exiter::exit_program("ERROR: Sorting feature failed! \"$unique\" already sorted.", "sort_feature"); } $sorted->{$unique} = 1; } } collect_modules_recursive($modulesref, $gid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted); } } } ################################################################################# # Sorting the feature in specified order. Evaluated is the key "Sortkey", that # is set in scp2 projects. # The display order of modules in Windows Installer is dependent from the order # in the idt file. Therefore the order of the modules array has to be adapted # to the Sortkey order, before the idt file is created. ################################################################################# sub sort_feature { my ($modulesref) = @_; my @feature = (); my %directaccess = (); my %directparent = (); my %directgid = (); my %directsortkey = (); my %sorted = (); for ( my $i = 0; $i <= $#{$modulesref}; $i++ ) { my $onefeature = ${$modulesref}[$i]; my $uniquekey = $onefeature->{'uniquekey'}; my $modulegid = $onefeature->{'gid'}; $directaccess{$uniquekey} = $i; $directgid{$uniquekey} = $onefeature->{'gid'}; # ParentID and Sortkey are not saved for the 'uniquekey', but only for the 'gid' if ( $onefeature->{'ParentID'} ) { $directparent{$modulegid} = $onefeature->{'ParentID'}; } else { $directparent{$modulegid} = ""; } if ( $onefeature->{'Sortkey'} ) { $directsortkey{$modulegid} = $onefeature->{'Sortkey'}; } else { $directsortkey{$modulegid} = "9999"; } # Bookkeeping: $sorted{$uniquekey} = 0; } # Searching all feature recursively, beginning with ParentID = "" my $parentid = ""; collect_modules_recursive($modulesref, $parentid, \@feature, \%directaccess, \%directgid, \%directparent, \%directsortkey, \%sorted); # Bookkeeping my $modulekey; foreach $modulekey ( keys %sorted ) { if ( $sorted{$modulekey} == 0 ) { $installer::logger::Lang->printf( "Warning: Module \"%s\" could not be sorted. Added to the end of the module array.\n", $modulekey); push(@feature, ${$modulesref}[$directaccess{$modulekey}]); } } return \@feature; } ################################################################################# # Adding a unique key to the modules array. The gid is not unique for # multilingual modules. Only the combination from gid and specific language # is unique. Uniqueness is required for sorting mechanism. ################################################################################# sub add_uniquekey { my ( $modulesref ) = @_; for ( my $i = 0; $i <= $#{$modulesref}; $i++ ) { my $uniquekey = ${$modulesref}[$i]->{'gid'}; if ( ${$modulesref}[$i]->{'specificlanguage'} ) { $uniquekey = $uniquekey . "_" . ${$modulesref}[$i]->{'specificlanguage'}; } ${$modulesref}[$i]->{'uniquekey'} = $uniquekey; } } ################################################################################# # Creating the file Feature.idt dynamically # Content: # Feature Feature_Parent Title Description Display Level Directory_ Attributes ################################################################################# sub prepare_feature_table ($$$) { my ($modules, $language, $variables) = @_; my $features = []; foreach my $onefeature (@$modules) { # Java and Ada only, if the correct settings are set my $styles = $onefeature->{'Styles'}; $styles = "" unless defined $styles; if (( $styles =~ /\bJAVAMODULE\b/ ) && ( ! ($variables->{'JAVAPRODUCT'} ))) { next; } # Controlling the language! # Only language independent feature or feature with the correct language will be included into the table next if $onefeature->{'ismultilingual'} && ($onefeature->{'specificlanguage'} ne $language); my $feature_gid =get_feature_gid($onefeature); my $feature = { 'Feature' => $feature_gid, 'Feature_Parent' => get_feature_parent($onefeature), 'Title' => $onefeature->{'Name'}, 'Description' => $onefeature->{'Description'}, 'Display' => get_feature_display($onefeature), 'Level' => get_feature_level($onefeature), 'Directory_' => get_feature_directory($onefeature), 'Attributes' => get_feature_attributes($onefeature) }; push @$features, $feature; # collecting all feature in global feature collector (so that properties can be set in property table) $installer::globals::featurecollector{$feature_gid} = 1; # collecting all language feature in feature collector for check of language selection if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && $onefeature->{'ParentID'} ne $installer::globals::rootmodulegid) { $installer::globals::multilingual_only_modules{$feature_gid} = 1; } # collecting all application feature in global feature collector for check of application selection if ( $styles =~ /\bAPPLICATIONMODULE\b/ ) { $installer::globals::application_modules{$feature_gid} = 1; } } return $features; } =head add_missing_features($features) When we are building a release, then there may be features missing that where present in the source release. As missing features would prevent patches from being created, we add the missing features. The returned feature hash is either identical to the given $features or is a copy with the missing features added. =cut sub add_missing_features ($) { my ($features) = @_; return $features if ! $installer::globals::is_release; # Acquire the feature list of the source release. my $source_feature_table = $installer::globals::source_msi->GetTable("Feature"); my $feature_column_index = $source_feature_table->GetColumnIndex("Feature"); # Prepare fast lookup of the target features. my %target_feature_map = map {$_->{'Feature'} => $_} @$features; # Find missing features. my @missing_features = (); foreach my $source_feature_row (@{$source_feature_table->GetAllRows()}) { my $feature_gid = $source_feature_row->GetValue($feature_column_index); if ( ! defined $target_feature_map{$feature_gid}) { push @missing_features, $source_feature_row; } } # Return when there are no missing features. return $features if scalar @missing_features==0; # Process the missing features. my $extended_features = [@$features]; foreach my $missing_feature_row (@missing_features) { my %feature = map {$_ => $missing_feature_row->GetValue($_)} ('Feature', 'Feature_Parent', 'Title', 'Description', 'Display', 'Level', 'Directory_', 'Attributes'); push @$extended_features, \%feature; $installer::logger::Lang->printf("added missing feature %s\n", $feature->{'Feature'}); } return $extended_features; } sub create_feature_table ($$$) { my ($basedir, $language, $features) = @_; my @feature_table = (); installer::windows::idtglobal::write_idt_header(\@feature_table, "feature"); foreach my $feature (@$features) { my $line = join("\t", $feature->{'Feature'}, $feature->{'Feature_Parent'}, $feature->{'Title'}, $feature->{'Description'}, $feature->{'Display'}, $feature->{'Level'}, $feature->{'Directory_'}, $feature->{'Attributes'}) . "\n"; push(@feature_table, $line); } my $filename = $basedir . $installer::globals::separator . "Feature.idt" . "." . $language; installer::files::save_file($filename ,\@feature_table); $installer::logger::Lang->printf("Created idt file: %s\n", $filename); } 1;