|
1 # Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 # All rights reserved. |
|
3 # This component and the accompanying materials are made available |
|
4 # under the terms of the License "Eclipse Public License v1.0" |
|
5 # which accompanies this distribution, and is available |
|
6 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 # |
|
8 # Initial Contributors: |
|
9 # Nokia Corporation - initial contribution. |
|
10 # |
|
11 # Contributors: |
|
12 # |
|
13 # Description: |
|
14 # |
|
15 # |
|
16 # Description: |
|
17 # Symbian::CBR::ApplyDelta.pm |
|
18 # |
|
19 |
|
20 package Symbian::CBR::ApplyDelta; |
|
21 |
|
22 use strict; |
|
23 use File::Basename; |
|
24 use FindBin qw($Bin); |
|
25 use File::Spec; |
|
26 use Symbian::CBR::Component::Manifest; |
|
27 use Symbian::CBR::DeltaRelease::Manifest qw(META_FILES DELTA_MANIFEST_FILE); |
|
28 use ExportData; |
|
29 use EnvDb; |
|
30 use Utils; |
|
31 use File::Path; |
|
32 use File::Temp; |
|
33 use XML::Simple; |
|
34 use File::Copy; |
|
35 use Carp; |
|
36 use Cwd; |
|
37 |
|
38 |
|
39 sub new { |
|
40 my $pkg = shift; |
|
41 my $iniData = shift; |
|
42 my $verbose = shift; |
|
43 my $self; |
|
44 $self->{iniData} = $iniData; |
|
45 $self->{verbose} = $verbose; |
|
46 bless $self, $pkg; |
|
47 return $self; |
|
48 } |
|
49 |
|
50 |
|
51 sub ReconstructEnv { |
|
52 my $self = shift; |
|
53 my $zipFile = shift; |
|
54 my $overwrite = shift; |
|
55 |
|
56 $self->{deltaManifest} = Symbian::CBR::DeltaRelease::Manifest->new($self->{verbose}); |
|
57 Utils::InitialiseTempDir($self->{iniData}); |
|
58 $self->{deltaDir} = Utils::TempDir(); |
|
59 |
|
60 my $deltaManifestFile = File::Spec->catfile($self->{deltaDir}, DELTA_MANIFEST_FILE ); |
|
61 print "Extracting delta release package.\n"; |
|
62 eval {Utils::Unzip($zipFile,$self->{deltaDir},0,1);}; |
|
63 croak "Error: Couldn't Extract File $zipFile $@\n" if($@); |
|
64 #Read delta manifest file. |
|
65 |
|
66 $self->{deltaManifest}->LoadManifest($deltaManifestFile); |
|
67 my $referenceBaseline = $self->{deltaManifest}->GetReferenceBaselineComp(); |
|
68 my $referenceVersion = $self->{deltaManifest}->GetReferenceBaselineVer(); |
|
69 my $destination = $self->{iniData}->PathData->LocalArchivePathForExistingComponent($referenceBaseline, $referenceVersion); |
|
70 croak "Error: Reference baseline $referenceBaseline $referenceVersion does not exist.\n" unless (defined $destination); |
|
71 my $index = index($destination, $referenceBaseline); |
|
72 $destination = substr($destination, 0, ($index)); |
|
73 foreach my $comp (sort keys %{$self->{deltaManifest}->ListAllComponents()}) { |
|
74 my $compStatus = $self->{deltaManifest}->GetCompStatus($comp); |
|
75 my $hasError; |
|
76 if ($compStatus eq "modified") { |
|
77 $hasError = $self->ReconstructComp($comp, $destination, $overwrite); #Reconstruct modified component. |
|
78 } |
|
79 elsif ($compStatus eq "added") { |
|
80 $hasError = $self->CopyCompToBaseline($comp, $destination, $overwrite); #Directly copy component to baseline. |
|
81 } |
|
82 |
|
83 if ($hasError) { |
|
84 my $version = $self->{deltaManifest}->GetCompNominatedVer($comp); |
|
85 print "Error: Can't reconstruct component $comp, version $version\n"; |
|
86 next; |
|
87 } |
|
88 } |
|
89 rmtree($self->{deltaDir}) or print "Warning: Couldn't delete temp directory ".$self->{deltaDir}.".\n"; |
|
90 } |
|
91 |
|
92 sub CopyCompToBaseline { |
|
93 my $self = shift; |
|
94 my $comp = shift; |
|
95 my $destination = shift; |
|
96 my $overwrite = shift; |
|
97 |
|
98 print "$comp is newly added to the baseline.\n"; |
|
99 my $tempNewCompPath = File::Spec->catfile($self->{deltaDir},"new", $comp); |
|
100 my $nomVersion = $self->{deltaManifest}->GetCompNominatedVer($comp); |
|
101 my $archiveCompPath = File::Spec->catdir($destination, $comp, $nomVersion); |
|
102 if (-d $archiveCompPath and !$overwrite) { |
|
103 print "Error: $comp already exists. Please use -o option to overwrite.\n"; |
|
104 return 1; |
|
105 } |
|
106 mkpath($archiveCompPath) unless (-d $archiveCompPath); |
|
107 foreach my $thisFile (@{Utils::ReadDir($tempNewCompPath)}) { |
|
108 my $thisTempFile = File::Spec->catfile($tempNewCompPath, $thisFile); |
|
109 my $thisArchivepFile = File::Spec->catfile($archiveCompPath, $thisFile); |
|
110 if (-e $thisArchivepFile) { |
|
111 print "Overwriting $thisFile.\n " if ($self->{verbose}); |
|
112 unless (unlink($thisArchivepFile)) { |
|
113 print "Error: Couldn't delete $thisArchivepFile : $!\n"; |
|
114 return 1; |
|
115 } |
|
116 } |
|
117 unless (copy($thisTempFile, $thisArchivepFile)) { |
|
118 print "Error: Couldn't copy file from $thisTempFile to $thisArchivepFile.\n"; |
|
119 return 1; |
|
120 } |
|
121 } |
|
122 return 0; |
|
123 } |
|
124 |
|
125 sub ReconstructComp { |
|
126 my $self = shift; |
|
127 my $comp = shift; |
|
128 my $destination = shift; |
|
129 my $overwrite = shift; |
|
130 |
|
131 my $refVersion = $self->{deltaManifest}->GetCompReferenceVer($comp); |
|
132 my $nomVersion = $self->{deltaManifest}->GetCompNominatedVer($comp); |
|
133 print "Reconstructing $comp component.\n"; |
|
134 my $refCompVer = File::Spec->catdir($destination, $comp, $refVersion); |
|
135 my $nomCompVer = File::Spec->catdir($destination, $comp, $nomVersion); |
|
136 if (-d $nomCompVer and !$overwrite) { |
|
137 print "Error: $comp of $nomVersion version already exists. Please use -o option to overwrite.\n"; |
|
138 return 1; |
|
139 } |
|
140 if (-d $nomCompVer) { |
|
141 print "Overwriting $comp\n" if($self->{verbose}); |
|
142 my $origDir = cwd(); |
|
143 |
|
144 chdir(dirname($nomCompVer)); #If you try to rmtree a UNC path the cwd must also be a UNC path |
|
145 unless (rmtree($nomCompVer)) { |
|
146 print "Error: Couldn't delete $nomCompVer directory\n"; |
|
147 return 1; |
|
148 } |
|
149 chdir($origDir); |
|
150 } |
|
151 mkpath($nomCompVer); |
|
152 #Make copy of reference version. |
|
153 foreach my $thisFile (@{Utils::ReadDir($refCompVer)}) { |
|
154 my $thisRefFile = File::Spec->catfile($refCompVer, $thisFile); |
|
155 my $thisNomFile = File::Spec->catfile($nomCompVer, $thisFile); |
|
156 unless (copy($thisRefFile, $thisNomFile)) { |
|
157 print "Error: Couldn't copy file from $thisRefFile to $thisNomFile. $!\n"; |
|
158 return 1; |
|
159 } |
|
160 } |
|
161 |
|
162 #Reconstruct modified zip files, copy newly added zip files and delete deleted zip files. |
|
163 foreach my $zipfile (keys %{$self->{deltaManifest}->GetZipFilesForComp($comp)}) { |
|
164 my $zipStatus = $self->{deltaManifest}->GetZipStatus($comp, $zipfile); |
|
165 my $nomZipFile = File::Spec->catfile($nomCompVer, $zipfile); |
|
166 if ($zipStatus eq "modified") { |
|
167 my $hasError = $self->ReconstructZipFile($comp, $zipfile, $nomCompVer); #If zip file is modified, then reconstruct it. |
|
168 return $hasError if($hasError); |
|
169 } |
|
170 |
|
171 elsif ($zipStatus eq "added") { |
|
172 my $tempZipFile = File::Spec->catfile(Utils::TempDir(),"modified","addedZips",$comp,$zipfile); |
|
173 if (-e $nomZipFile) { |
|
174 print "Overwriting $nomZipFile.\n " if ($self->{verbose}); |
|
175 unless (unlink($nomZipFile)) { |
|
176 print "Error: Couldn't delete $nomZipFile : $!\n"; |
|
177 return 1; |
|
178 } |
|
179 } |
|
180 unless (copy($tempZipFile, $nomZipFile)) { |
|
181 print "Error: Couldn't copy $tempZipFile to $nomZipFile. $!\n"; |
|
182 return 1; |
|
183 } |
|
184 } |
|
185 elsif ($zipStatus eq "deleted") { |
|
186 if (-e $nomZipFile) { |
|
187 unless (unlink($nomZipFile)) { |
|
188 print "Error: Couldn't delete $nomZipFile : $!\n"; |
|
189 return 1; |
|
190 } |
|
191 } |
|
192 } |
|
193 elsif ($zipStatus eq "identical") { |
|
194 print "$zipfile is not modified.\n" if($self->{verbose} > 1); |
|
195 } |
|
196 else { |
|
197 print "Error: Unknown zip file status \"$zipStatus\" for $zipfile of $comp component in delta manifest file.\n"; |
|
198 return 1; |
|
199 } |
|
200 } |
|
201 #Reconstruct reldata, manifest.xml and exports.txt files. |
|
202 my $deltaFilePath = File::Spec->catdir($self->{deltaDir}, META_FILES); |
|
203 foreach my $metafile (keys %{$self->{deltaManifest}->GetMetaFiles($comp)}) { |
|
204 my $nomFile = File::Spec->catfile($nomCompVer, $metafile); |
|
205 my $deltaFile = $comp."_".$metafile; |
|
206 $deltaFile = File::Spec->catfile($deltaFilePath, $deltaFile); |
|
207 #retry 10 times |
|
208 my $retries = 10; |
|
209 while ($retries > 0) { |
|
210 if (-e $nomFile) { |
|
211 unlink($nomFile) or print "Warning: delete file $nomFile failed. $?, $!\n"; |
|
212 } |
|
213 print "Copying $metafile.\n" if( -e $metafile and $self->{verbose}); |
|
214 if (copy($deltaFile, $nomFile) == 0) { |
|
215 #copy failed, warning and try again |
|
216 print "Warning: Couldn't copy file from $deltaFile to $nomFile. $!\n"; |
|
217 $retries--; |
|
218 } |
|
219 else { |
|
220 #copy successfully, jump out of the loop |
|
221 last; |
|
222 } |
|
223 } |
|
224 if ($retries<=0) { |
|
225 print "Error: Couldn't copy file $deltaFile to $nomFile. $!\n"; |
|
226 return 1; |
|
227 } |
|
228 } |
|
229 |
|
230 return 0; |
|
231 } |
|
232 |
|
233 |
|
234 sub ReconstructZipFile { |
|
235 my $self = shift; |
|
236 my $comp = shift; |
|
237 my $zipfile = shift; |
|
238 my $releaseFolder = shift; |
|
239 |
|
240 my $nomZipFile = File::Spec->catfile($releaseFolder, $zipfile); |
|
241 my $tempCompPath = mkdtemp($self->{iniData}->TempDir().'\_XXXX'); |
|
242 mkpath ($tempCompPath) unless(-d $tempCompPath); |
|
243 my $tempCompZips = File::Spec->catdir($self->{deltaDir}, "TempZips"); |
|
244 mkpath($tempCompZips) unless(-d $tempCompZips); |
|
245 my $tempCompZipFile = File::Spec->catdir($tempCompZips, $zipfile); |
|
246 #Move zip file to temporary location. |
|
247 unless (move($nomZipFile, $tempCompZipFile)) { |
|
248 print "Error: Couldn't move $zipfile to temp directory. $!\n"; |
|
249 return 1; |
|
250 } |
|
251 print "Extracting $zipfile file.\n" if($self->{verbose} > 1); |
|
252 Utils::Unzip($tempCompZipFile,$tempCompPath,0,1); |
|
253 unless (unlink($tempCompZipFile)) { |
|
254 print "Error: Couldn't delete $tempCompZipFile : $!\n"; |
|
255 return 1; |
|
256 } |
|
257 |
|
258 foreach my $file (keys %{$self->{deltaManifest}->GetFilesForZip($comp, $zipfile)}) { |
|
259 my $deltaFilePath = File::Spec->catfile($self->{deltaDir},"modified",$file); |
|
260 my $tempCompFilePath = File::Spec->catfile($tempCompPath,$file); |
|
261 my $fileStatus = $self->{deltaManifest}->GetFileStatus($comp, $zipfile, $file); |
|
262 my $type = $self->{deltaManifest}->GetFileType($comp, $zipfile, $file ); |
|
263 |
|
264 if ($fileStatus eq "added") { |
|
265 print "Copying $file\n" if($self->{verbose}); |
|
266 my $tempFilePath = dirname ($tempCompFilePath); |
|
267 unless (-e $tempFilePath) { |
|
268 unless (mkpath ($tempFilePath)) { |
|
269 print "Error: Unable to create $tempFilePath path.\n"; |
|
270 return 1; |
|
271 } |
|
272 } |
|
273 unless (-e $deltaFilePath) { |
|
274 print "Error: $deltaFilePath file doesn't exists.\n"; |
|
275 return 1; |
|
276 } |
|
277 unless (copy($deltaFilePath, $tempCompFilePath)) { |
|
278 print "Error: Couldn't copy file from $deltaFilePath to $tempCompFilePath\n"; |
|
279 return 1; |
|
280 } |
|
281 } |
|
282 elsif ($fileStatus eq "modified") { |
|
283 if ($type eq "file") { |
|
284 if (-e $tempCompFilePath) { |
|
285 unless (unlink($tempCompFilePath)) { |
|
286 print "Error: Couldn't delete $tempCompFilePath : $!\n"; |
|
287 return 1; |
|
288 } |
|
289 } |
|
290 my $tempFilePath = dirname ($tempCompFilePath); |
|
291 unless (-e $tempFilePath) { |
|
292 mkpath ($tempFilePath) or croak "Error: Unable to create $tempFilePath path.\n"; |
|
293 } |
|
294 unless (-e $deltaFilePath) { |
|
295 print "Error: $deltaFilePath file doesn't exist.\n"; |
|
296 return 1; |
|
297 } |
|
298 unless (copy ($deltaFilePath, $tempCompFilePath)) { |
|
299 print "Error: Couldn't copy file from $deltaFilePath to $tempCompFilePath\n"; |
|
300 return 1; |
|
301 } |
|
302 } |
|
303 elsif ($type eq "delta") { |
|
304 my $deltaFile = $deltaFilePath.".delta"; |
|
305 $self->ReconstructFile($tempCompFilePath, $deltaFile, $tempCompFilePath); |
|
306 } |
|
307 else { |
|
308 print "Error: Unknown file type \"$type\" in delta manifest file.\n"; |
|
309 return 1; |
|
310 } |
|
311 } |
|
312 elsif ($fileStatus eq "deleted") { |
|
313 if (unlink($tempCompFilePath) == 0) { |
|
314 if (-e $tempCompFilePath) { |
|
315 print "Error: Couldn't delete $tempCompFilePath : $!\n"; |
|
316 return 1; |
|
317 } |
|
318 else { |
|
319 print "Warning: Expecting to delete $tempCompFilePath, but it does not exist.\n"; |
|
320 } |
|
321 } |
|
322 } |
|
323 else { |
|
324 print "Error: Unknown file status \"$fileStatus\" for \"$file\" file.\n"; |
|
325 return 1; |
|
326 } |
|
327 } |
|
328 #Pack all files of a zipfile to form a category.zip file. |
|
329 my @allFiles; |
|
330 my @filesToBeZipped; |
|
331 Utils::ListAllFiles($tempCompPath, \@allFiles); |
|
332 foreach my $thisFile (@allFiles) { |
|
333 my $file = substr($thisFile, (length($tempCompPath)+1)); |
|
334 push @filesToBeZipped, $file; |
|
335 } |
|
336 Utils::ZipList( $nomZipFile, \@filesToBeZipped, $self->{verbose}, undef,$tempCompPath); |
|
337 unless (rmtree ($tempCompPath)) { |
|
338 print "Error: Couldn't delete $tempCompPath directory.\n"; |
|
339 return 1; |
|
340 } |
|
341 |
|
342 return 0; |
|
343 } |
|
344 |
|
345 sub ReconstructFile { |
|
346 my $self = shift; |
|
347 my $referenceFile = shift; |
|
348 my $deltaFilePath = shift; |
|
349 my $destination = shift; |
|
350 |
|
351 my $destinationDir = dirname($destination); |
|
352 mkpath($destinationDir) unless(-d $destinationDir); |
|
353 print "Reconstructing ".basename($referenceFile)." file.\n" if($self->{verbose} > 1); |
|
354 my $status = system "zdu \"$referenceFile\" \"$deltaFilePath\" \"$destination\"" ; |
|
355 if( $status != 0 ) { |
|
356 $status = system "zdu" ; |
|
357 $! = $? >> 8; |
|
358 if( $status != 0 ) { |
|
359 print "Error: The zdelta tool is not installed. Please install zdelta.\n"; |
|
360 croak; |
|
361 } |
|
362 else { |
|
363 print "Error: The zdelta tool is not installed properly. Please install zdelta once again.\n"; |
|
364 croak; |
|
365 } |
|
366 } |
|
367 } |
|
368 |
|
369 1; |
|
370 |
|
371 __END__ |
|
372 |
|
373 |
|
374 =head1 NAME |
|
375 |
|
376 Symbian::CBR::ApplyDelta.pm - Reconstructs the nominated baseline using deltas and reference version of baseline. |
|
377 |
|
378 =head2 new |
|
379 |
|
380 Expects to be passed an C<IniData> reference and verbosity level. Creates an ApplyDelta object. |
|
381 |
|
382 =head2 ReconstructEnv |
|
383 |
|
384 Expects to be passed a delta zip file path, a destination archive where the environment is to be created, and an overwrite flag specifying whether existing components should be overwritten or not. It makes use of the delta zip file and a reference baseline (specified by the delta manifest file) and reconstructs the originally nominated baseline. |
|
385 |
|
386 =head2 ReconstructComp |
|
387 |
|
388 Expects to be passed a component name, a destination and an overwrite flag specifying whether existing component should be overwritten or not. It makes use of the delta for a component and a reference version of a component (specified by the delta manifest file) and reconstructs the originally nominated version of a component. |
|
389 |
|
390 =head2 ReconstructFile |
|
391 |
|
392 Expects to be passed a reference file, path to a delta file and a destination path. Makes use of the zdelta third party tool to reconstruct the originally nominated version of the file from the inputs. |
|
393 |
|
394 =head1 KNOWN BUGS |
|
395 |
|
396 None. |
|
397 |
|
398 =head1 COPYRIGHT |
|
399 |
|
400 Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
401 All rights reserved. |
|
402 This component and the accompanying materials are made available |
|
403 under the terms of the License "Eclipse Public License v1.0" |
|
404 which accompanies this distribution, and is available |
|
405 at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
406 |
|
407 Initial Contributors: |
|
408 Nokia Corporation - initial contribution. |
|
409 |
|
410 Contributors: |
|
411 |
|
412 Description: |
|
413 |
|
414 |
|
415 =cut |