2007-11-16 Marcus Brinkmann <marcus@g10code.de>
[gpg4win.git] / src / make-msi.pl
1 #! /usr/bin/perl -w
2 # make-msi.pl - MSI Installer for GnuPG 4 Windows.
3 # Copyright (C) 2007 g10 Code GmbH
4
5 # This file is part of Gpg4win.
6
7 # Gpg4win is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11
12 # Gpg4win is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20
21 # Invoke like this:
22 #
23 # perl make-msi.pl < ../include/config.nsi
24 #
25 # Note that this needs to be called from the gpg4win src/ directory, because
26 # a number of files (inst-*.nsi for example) are accessed in that directory.
27 #
28 # This program parses the NSIS source files and creates a .wix file,
29 # which needs to be compiled with candle and linked with light, see
30 # make-msi.bat.
31 #
32 # The file make-msi.guids is read and updated.  It contains GUIDs for all
33 # components used by the installer, which are kept from version to version.
34 #
35 # Also, the output file make-msi.files contains a list of all files
36 # that will be accessed by the linker when creating the package.
37
38 use strict;
39
40 # TODO:
41 #
42 # DirMngr config files/cache directory?  service start fails!!!
43 # desktop and quick launch entries, but optional (also startmenu optional)
44
45 # The list of all enabled packages.
46 @::pkgs = ();
47 # All definitions from config.nsi (the input file)
48 %::config = ();
49 # A description of the components.  We have one component per package.
50 # Creating the data for these components is most of the work.
51 @::components = ();
52 # A list of all source files included in the package.
53 @::sources = ();
54 # A hash which maps frobbed package names to a hash of frobbed package
55 # names on which they depend.
56 %::deps = ();
57 # A hash which contains one key for each file that wants a shortcut in the
58 # canonical places (start menu, desktop, quick launch).
59 %::shortcuts = ();
60
61 $::INSTDIR = 'GnuPG';
62 $::name = 'GnuPG for Windows';
63
64 # Simple indentation tracking, for pretty printing.
65 $::level = 0;
66
67 \f
68 # FIXME: Some work arounds for the manual.
69
70 my $DESC_Name_man_advanced_de = "Advanced Manual (German)";
71 my $DESC_Name_man_advanced_en = "Advanced Manual";
72 my $DESC_Name_man_novice_de = "Novice Manual (German)";
73 my $DESC_Name_man_novice_en = "Novice Manual";
74
75 \f
76 # We use a new product and package code for every build (using pseudo
77 # components), but a single constant upgrade code for all versions.
78 # Note that Windows installer ignores the build part of the version
79 # number (only the first three components are used).
80 #
81 # FIXME: Build upgrade table.
82 #
83 # We take a simplified view: Each file corresponds to exactly one
84 # component.  This means we need to generate GUIDs for these
85 # components and remember them from one installer version to the next.
86 # We do this automatically by means of a support file, make-msi.guids.
87
88 %::guid = ();
89 $::guid_file = 'make-msi.guids';
90 $::guid_changed = 0;
91
92 sub fetch_guids
93 {
94     # FIXME: Check if file exists.
95     open (FILE, "<$::guid_file") or return;
96     while (<FILE>)
97     {
98         next if (/^#/);
99         if (/(\S+)\s+(.+)\s*\r?\n$/)
100         {
101             $::guid{$2} = $1;
102         }
103     }
104     close (FILE);
105 }
106
107
108 sub store_guids
109 {
110     # FIXME: Maybe allow to forget unused GUIDs.
111
112     return if (not $::guid_changed);
113     print STDERR "GUID list stored in $::guid_file changed, please commit!\n";
114     open (FILE, ">$::guid_file.bak") or die;
115     print FILE "# This is an automatically generated file.  DO NOT EDIT.\n";
116     foreach my $file (sort keys %::guid)
117     {
118         print FILE "$::guid{$file} $file\n";
119     }
120     close FILE;
121     rename "$::guid_file.bak", $::guid_file or die;
122 }
123
124
125 sub get_guid
126 {
127     my ($file) = @_;
128     my $guid;
129
130     if (defined $::guid{$file})
131     {
132         return $::guid{$file};
133     }
134     # Need to generate a new GUID.
135     $::guid_changed = 1;
136     $guid = `uuidgen`;
137     chomp $guid;
138     $::guid{$file} = $guid;
139     return $guid;
140 }
141
142 \f
143 $::files_file = 'make-msi.files';
144
145 # We store the list of included files for temporary packaging, in case
146 # WiX needs to be run on a different system.
147 sub store_files
148 {
149     open (FILE, ">$::files_file") or die;
150     foreach my $pkg (@::components)
151     {
152         next if ($#{$pkg->{files}} == -1);
153         print FILE (join ("\n", map { "src/" . ($_->{source}) }
154                           @{$pkg->{files}})). "\n";
155     }
156     close FILE;
157 }
158
159 \f
160 sub get_deps
161 {
162     my $name = '';
163     my %deps = ();
164
165     # FIXME: Check if file exists.
166     open (FILE, "<inst-sections.nsi") or return;
167     while (<FILE>)
168     {
169         my $line = $_;
170
171         if ($name eq '')
172         {
173             if ($line =~ m/^\s*have_(\S+):\s*\r?\n$/)
174             {
175                 $name = $1;
176             }
177         }
178         else
179         {
180             if ($line =~ m/^\s*!insertmacro\s+SelectSection\s+\$\{SEC_(\S+)\}\s*\r?\n$/)
181             {
182                 $deps{$1} = 1;
183             }
184             elsif ($line =~ m/^\s*skip_$name:\s*\r?\n$/)
185             {
186                 # We resolve indirect dependencies right now.
187                 foreach my $pkg (keys %::deps)
188                 {
189                     if (defined $::deps{$pkg}->{$name})
190                     {
191                         foreach my $dep (keys %deps)
192                         {
193                             $::deps{$pkg}->{$dep} = $::deps{$pkg}->{$name} + 1
194                                 if (not defined $::deps{$pkg}->{$dep})
195                         }
196                     }
197                 }
198                 $::deps{$name} = { %deps };
199                 $name = '';
200                 %deps = ();
201             }
202         }
203     }
204     close (FILE);
205 }
206
207 \f
208 sub get_shortcuts
209 {
210     my %shortcuts = ();
211
212     # Pending line.
213     my $line;
214
215     # FIXME: Check if file exists.
216     open (FILE, "<inst-sections.nsi") or return;
217     while (<FILE>)
218     {
219         # Combine multiple lines connected with backslashes.
220         $line = $line . $_;
221         if ($line =~ m/^(.*)\\\s*\r?\n$/)
222         {
223             $line = $1 . ' ';
224             next;
225         }
226         $_ = $line;
227         $line = '';
228
229         if (m,^\s*CreateShortCut\s+\"\$SMPROGRAMS\\\$STARTMENU_FOLDER\\[^.]+\.lnk\"\s+\"\$INSTDIR\\([^"]+)\",)
230         {
231             $shortcuts{$1} = 1;
232         }
233     }
234     close (FILE);
235     %::shortcuts = %shortcuts;
236 }
237
238 \f
239 sub collect_all
240 {
241   # Input file is $(top_srcdir)/include/config.nsi
242
243   while (<>)
244   {
245       if (/^!define\s+(\w+)\s+(\S.*\S)\s*\r?\n$/)
246       {
247           $::config{$1} = $2;
248       }
249   }
250   # gpg4win_build_list is a C-like string, so strip the quotes first.
251   my $pkg_list;
252   eval '$pkg_list = ' . $::config{gpg4win_build_list};
253   @::pkgs = split (' ', $pkg_list);
254
255   # Now we have a list of packages to process.  For each package,
256   # create a nice data structure that captures all the information we
257   # collect.
258
259   foreach my $pkg (@::pkgs)
260   {
261       my %pkg;
262       $pkg{name} = $pkg;
263       $pkg =~ tr/-+/__/;
264       $pkg{frobbed_name} = $pkg;
265       $pkg{version} = $::config{"gpg4win_pkg_${pkg}_version"};
266       $pkg{source} = $::config{"gpg4win_pkg_${pkg}"};
267       $pkg{features} = '';
268       $pkg{hidden} = 0;
269
270       # We parse the inst-package file to figure out what to do.  This
271       # is not a full-featured NSIS to MSI converter, but it does the
272       # job for us.
273
274       my $prefix;
275       if (defined $pkg{version})
276       {
277           $prefix = "playground/install/pkgs/$pkg{name}-$pkg{version}";
278       }
279
280       # The list of all files encountered and included in the package.
281       my @files;
282       # The list of all registry settings to write.
283       my @registry;
284
285       # The current directory.
286       my $dir = '';
287
288       # The pending line.
289       my $line = '';
290
291       open (FILE, "<inst-$pkg{name}.nsi") or die;
292       while (<FILE>)
293       {
294           # Combine multiple lines connected with backslashes.
295           $line = $line . $_;
296           if ($line =~ m/^(.*)\\\s*\r?\n$/)
297           {
298               $line = $1 . ' ';
299               next;
300           }
301           $_ = $line;
302           $line = '';
303
304           # FIXME: Handle hidden packages "-foo".
305           if (m,^\s*Section\s+"-([^"]+)",)
306           {
307               # Hidden packages are dependency-tracked.
308               $pkg{title} = $1;
309               $pkg{level} = 2000;  # Superfluous.
310               $pkg{hidden} = 1;
311           }
312           elsif (m,^\s*Section\s+"([^"]+)",)
313           {
314               # FIXME: Work around for manuals, which have variables
315               # in this place.
316               my $title = $1;
317               $title =~ s/^\$\((.*)\)$/\$$1/;
318               eval '$pkg{title} = "' . $title . '"';
319               $pkg{level} = 1;
320           }
321           elsif (m,^\s*Section\s+/o\s+"([^"]+)",)
322           {
323               # FIXME: Work around for manuals, which have variables
324               # in this place.
325               $pkg{title} = $1;
326               # Default install level is 3.
327               $pkg{level} = 1000;
328           }
329           elsif (m,^\s*LangString\s+DESC_SEC_\S+\s+\$\{LANG_ENGLISH\}\s+\"([^"]+)\"\s*\r?\n,)
330           {
331               $pkg{description} = $1;
332           }
333           # Special hack for kdesupport.nsi.  FIXME: Could do real
334           # variable substitution here.
335           elsif (m,^\s*\!define prefix \${ipdir}/([^\$]+)-\$.*\r?\n$,)
336           {
337               $prefix = "playground/install/pkgs/$1-$pkg{version}";
338           }
339           elsif (m,^\s*SetOutPath\s+"?\$INSTDIR\\?([^"]*)"?\s*\r?\n$,)
340           {
341               $dir = $1;
342           }
343           elsif (m,^\s*File\s+"?\$\{(prefix|BUILD_DIR|SRCDIR)\}(?:/(\S*))?/([^/"\s]+)"?\s*\r?\n$,)
344           {
345               my $source = $3;
346
347               $source = "$2/$source" if defined $2;
348               $source = "${prefix}/$source" if $1 eq 'prefix';
349               # FIXME: We assume that srcdir == build_dir here.
350
351               push @files, { source => $source, dir => $dir, target => $3 };
352               push @::sources, $source;
353           }
354           elsif (m,^\s*File\s+/oname=(\S+)\s+"?\$\{(prefix|BUILD_DIR)\}/([^"\s]+)"?\s*\r?\n$,)
355           {
356               my $target = $1;
357               my $source = $3;
358
359               $source = "${prefix}/$source" if $2 eq 'prefix';
360
361               # Temp files are due to overwrite attempts, which are
362               # handled automatically by the Windows Installer.
363               # Ignore them here.
364               next if $target =~ m/\.tmp$/;
365
366               push @files, { source => $source,
367                              dir => $dir, target => $target };
368               push @::sources, $source;
369           }
370           elsif (m,^\s*WriteRegStr\s+(\S+)\s+"([^"]+)"\s+"([^"]+)"\s+"?([^"]+)"?\s*\r?\n$,)
371           {
372               my ($root, $key, $name, $value) = ($1, $2, $3, $4);
373               $value =~ s/\$INSTDIR/\[INSTDIR\]/g;
374               push (@registry,
375                     { root => $root, key => $key, name => $name,
376                       value => $value, type => 'string' });
377           }
378       }
379       close (FILE);
380       $pkg{files} = \@files;
381       $pkg{registry} = \@registry;
382
383       # Some things we can not easily parse from the NSI files.  For
384       # these, we do manual overrides here.
385       if ($pkg{name} eq 'gnupg')
386       {
387           $pkg{features} .= " Absent='disallow'";
388       }
389       elsif ($pkg{name} eq 'gnupg2')
390       {
391           $pkg{features} .= " Absent='disallow'";
392       }
393
394       push @::components, \%pkg;
395   }
396 }
397
398
399 sub dump_all
400 {
401     my $pkg;
402     # A running count for files within each feature.
403     my $fileidx;
404     # A running count for registry settings within each feature.
405     my $regidx;
406     # A running count for directories throughout the whole file.
407     my $diridx = 0;
408     # The current directory.
409     my $cdir = '';
410
411     foreach $pkg (@::components)
412     {
413         $fileidx = 0;
414         foreach my $file (@{$pkg->{files}})
415         {
416             if ($cdir ne $file->{dir})
417             {
418                 # We need to change the directory.  We weed out empty
419                 # path elements, which also takes care of leading slashes.
420                 my @cdir = grep (!/^$/, split (/\\/, $cdir));
421                 my @ndir = grep (!/^$/, split (/\\/, $file->{dir}));
422                 my $min;
423                 my $i;
424                 $min = $#cdir;
425                 $min = $#ndir if ($#ndir < $min);
426                 for ($i = 0; $i <= $min; $i++)
427                 {
428                     last if ($cdir[$i] ne $ndir[$i])
429                 }
430                 my $j;
431                 for ($j = $i; $j <= $#cdir; $j++)
432                 {
433                     $::level -= 2;
434                     print ' ' x $::level
435                         . "</Directory>\n";
436                 }
437                 for ($j = $i; $j <= $#ndir; $j++)
438                 {
439                     print ' ' x $::level
440                         . "<Directory Id='d_$diridx' Name='$ndir[$j]'>\n";
441                     $diridx++;
442                     $::level += 2;
443                 }
444                 $cdir = $file->{dir};
445             }
446
447             my $targetfull;
448             if ($file->{dir} ne '')
449             {
450                 $targetfull = $file->{dir} . '\\' . $file->{target};
451             }
452             else
453             {
454                 $targetfull = $file->{target};
455             }
456
457             print ' ' x $::level
458                 . "<Component Id='c_$pkg->{frobbed_name}_$fileidx' Guid='"
459                 . get_guid ($targetfull) . "'>\n";
460             print ' ' x $::level
461                 . "  <File Id='f_$pkg->{frobbed_name}_$fileidx' Name='"
462                 . $file->{target} . "' Source='" . $file->{source} . "'"
463                 . " DefaultLanguage='1033'>\n";
464
465             # EXCEPTIONS:
466             if ($targetfull eq 'gpgol.dll')
467             {
468                 print ' ' x $::level
469                     . "    <Class Id='{42D30988-1A3A-11DA-C687-000D6080E735}' "
470                     . "Context='InprocServer32' Description='GpgOL - The "
471                     . "GnuPG Outlook Plugin' ThreadingModel='neutral'/>\n";
472             }
473             if ($targetfull eq 'gpgex.dll')
474             {
475                 print ' ' x $::level
476                     . "    <Class Id='{CCD955E4-5C16-4A33-AFDA-A8947A94946B}' "
477                     . "Context='InprocServer32' Description='GpgEX' "
478                     . "ThreadingModel='apartment'/>\n";
479             }
480             elsif ($targetfull eq 'gpgee.dll')
481             {
482                 print STDERR "ERR: run heat.exe on gpgee.dll and add info\n";
483                 exit 1;
484             }
485
486             # Create shortcuts.
487             if (defined $::shortcuts{$targetfull})
488             {
489                 print ' ' x $::level
490                     . "    <Shortcut Id='sm_$pkg->{frobbed_name}_$fileidx' "
491                     . "Directory='ProgramMenuDir' Name='$file->{target}'/>\n";
492
493 #               print ' ' x $::level
494 #                   . "    <Shortcut Id='sm_$pkg->{frobbed_name}_$fileidx' "
495 #                   . "Directory='DesktopFolder' Name='$file->{target}'/>\n";
496             }
497
498             print ' ' x $::level
499                 . "  </File>\n";
500
501             if (defined $::shortcuts{$targetfull})
502             {
503                 # http://www.mail-archive.com/wix-users@lists.sourceforge.net/msg02746.html
504                 # -sice:ICE64
505                 print ' ' x $::level
506                     . "    <RemoveFolder Id='rsm_$pkg->{frobbed_name}_$fileidx' "
507                     . "Directory='ProgramMenuDir' On='uninstall'/>\n";
508             }
509
510             # EXCEPTIONS:
511             # We use $targetfull because there is also a gpg.exe in pub\.
512             if ($targetfull eq 'gpg.exe')
513             {
514                 print ' ' x $::level
515                     . "  <Environment Id='env_path' Name='PATH' Action='set' "
516                     . "System='yes' Part='last' Value='[INSTDIR]pub'/>\n";
517             }
518             elsif ($targetfull eq 'gpgol.dll')
519             {
520                 print ' ' x $::level
521                     . "  <RegistryValue Root='HKLM' Key='Software\\"
522                     . "Microsoft\\Exchange\\Client\\Extensions' "
523                     . "Name='GpgOL' "
524                     . "Value='4.0;[!gpgol.dll];1;11000111111100;11111101' "
525                     . "Type='string' Action='write'/>\n";
526                 print ' ' x $::level
527                     . "  <RegistryValue Root='HKLM' Key='Software\\"
528                     . "Microsoft\\Exchange\\Client\\Extensions' "
529                     . "Name='Outlook Setup Extension' "
530                     . "Value='4.0;Outxxx.dll;7;000000000000000;0000000000;OutXXX' "
531                     . "Type='string' Action='write'/>\n";
532             }
533             elsif ($targetfull eq 'gpgex.dll')
534             {
535                 print ' ' x $::level
536                     . "  <ProgId Id='*'/>\n";
537                 print ' ' x $::level
538                     . "  <ProgId Id='Directory'/>\n";
539                 print ' ' x $::level
540                     . "  <RegistryValue Root='HKCR' "
541                     . "Key='*\\ShellEx\\ContextMenuHandlers\\GpgEX' "
542                     . "Value='{CCD955E4-5C16-4A33-AFDA-A8947A94946B}' "
543                     . "Type='string' Action='write'/>\n";
544                 print ' ' x $::level
545                     . "  <RegistryValue Root='HKCR' "
546                     . "Key='Directory\\ShellEx\\ContextMenuHandlers\\GpgEX' "
547                     . "Value='{CCD955E4-5C16-4A33-AFDA-A8947A94946B}' "
548                     . "Type='string' Action='write'/>\n";
549             }
550             elsif ($targetfull eq 'gpgee.dll')
551             {
552                 print STDERR "ERR: run heat.exe on gpgee.dll and add info\n";
553                 exit 1;
554             }
555             elsif ($targetfull eq 'dirmngr.exe')
556             {
557                 print ' ' x $::level
558                     . "  <ServiceInstall Id='s_dirmngr' "
559                     . "DisplayName='Directory Manager' "
560                     . "Name='DirMngr' ErrorControl='normal' Start='auto' "
561                     . "Arguments='--service' "
562                     . "Type='ownProcess' Vital='yes'/>\n";
563 # FIXME: Start service (currently broken).
564 #               print ' ' x $::level
565 #                   . "  <ServiceControl Id='s_dirmngr_ctrl' "
566 #                   . "Name='DirMngr' Start='install' Stop='uninstall' "
567 #                   . "Remove='uninstall'/>\n";
568             }
569
570             print ' ' x $::level
571                 . "</Component>\n";
572             $fileidx++;
573         }
574
575         $regidx = 0;
576         foreach my $reg (@{$pkg->{registry}})
577         {
578             my $target;
579
580             $target = '/REGISTRY/' . $reg->{root} . '/' . $reg->{key}
581             . '/' . $reg->{name};
582
583             print ' ' x $::level
584                 . "<Component Id='c_$pkg->{frobbed_name}_r_$regidx' Guid='"
585                 . get_guid ($target) . "'>\n";
586             print ' ' x $::level
587                 . "  <RegistryValue Id='r_$pkg->{frobbed_name}_$regidx' Root='"
588                 . $reg->{root} . "' Key='" . $reg->{key} . "' Name='"
589                 . $reg->{name} . "' Action='write' Type='" . $reg->{type}
590                 . "' Value='" . $reg->{value} . "'/>\n";
591             print ' ' x $::level
592                 . "</Component>\n";
593             $regidx++;
594         }
595     }
596 }
597
598
599 sub dump_meat
600 {
601     my ($pkg) = @_;
602     my $fileidx;
603     my $regidx;
604
605     $fileidx = 0;
606     foreach my $file (@{$pkg->{files}})
607     {
608         print ' ' x $::level
609             . "  <ComponentRef Id='c_$pkg->{frobbed_name}_$fileidx'/>\n";
610         $fileidx++;
611     }
612     $regidx = 0;
613     foreach my $reg (@{$pkg->{registry}})
614     {
615         print ' ' x $::level
616             . "  <ComponentRef Id='c_$pkg->{frobbed_name}_r_$regidx'/>\n";
617         $regidx++;
618     }
619 }
620
621
622 sub dump_all2
623 {
624     my $pkg;
625
626     foreach $pkg (@::components)
627     {
628         my $features;
629
630         next if $pkg->{hidden};
631
632         $features = $pkg->{features};
633 #       $features .= " Display='hidden'" if $pkg->{hidden};
634         $features .= " Description='$pkg->{description}'"
635             if $pkg->{description};
636         
637         print ' ' x $::level
638             . "<Feature Id='p_$pkg->{frobbed_name}' Level='$pkg->{level}' "
639             . "Title='$pkg->{title}'" . $features . ">\n";
640         dump_meat ($pkg);
641
642         foreach my $dep (keys %{$::deps{$pkg->{frobbed_name}}})
643         {
644             my $deppkg;
645
646             foreach my $_pkg (@::components)
647             {
648                 $deppkg = $_pkg;
649                 last if ($_pkg->{frobbed_name} eq $dep);
650             }
651             
652             print ' ' x $::level
653                 . "  <Feature Id='p_$pkg->{frobbed_name}_$dep' "
654                 . "Title='p_$pkg->{frobbed_name}_$dep' "
655                 . "Level='$pkg->{level}' Display='hidden' "
656                 . "InstallDefault='followParent'>\n";
657             $::level += 2;
658             dump_meat ($deppkg);
659             $::level -= 2;
660             print ' ' x $::level
661                 . "  </Feature>\n";
662         }
663         print ' ' x $::level
664             . "</Feature>\n";
665     }
666 }
667
668 \f
669 # WiX is the Windows Installer XML toolset.  It contains a compiler
670 # (candle.exe) and a linker (light.exe) which can assemble an XML file
671 # and data files to a Windows installer package (MSI).
672 #
673 # The following code creates an appropriate XML file for Gpg4win.
674
675 # We use a single media element, which is also the default for all
676 # components and directory elements.
677
678 # FIXME: Use Vital for all file attributes?
679 fetch_guids ();
680 collect_all ();
681 get_deps ();
682 get_shortcuts ();
683
684 $::product_id = get_guid ("/PRODUCT/$::config{_BUILD_FILEVERSION}");
685 $::upgrade_code = get_guid ("/UPGRADE/1");
686
687 print <<EOF;
688 <?xml version='1.0'?>
689 <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
690   <Product Name='Gpg4win'
691            Id='$::product_id'
692            UpgradeCode='$::upgrade_code'
693            Language='1033'
694            Version='$::config{_BUILD_FILEVERSION}'
695            Manufacturer='g10 Code GmbH'>
696     <Package Description='Gpg4win Installer'
697              Comments='http://www.gpg4win.org/'
698              Compressed='yes' 
699              InstallerVersion='200'
700              InstallPrivileges='elevated'
701              Manufacturer='g10 Code GmbH'/>
702
703     <Upgrade Id='$::upgrade_code'>
704       <UpgradeVersion Property='UPGRADEPROP'
705                       IncludeMaximum='no'
706                       Maximum='$::config{_BUILD_FILEVERSION}'/>
707     </Upgrade>
708
709     <InstallExecuteSequence>
710       <RemoveExistingProducts After='InstallFinalize' />
711     </InstallExecuteSequence>
712
713     <Condition
714      Message="You need to be an administrator to install this product.">
715       Privileged
716     </Condition>
717
718     <Media Id='1' Cabinet='gpg4win.cab' EmbedCab='yes'/>
719
720     <Property Id="INSTDIR">
721       <RegistrySearch Id='gpg4win_registry' Type='raw'
722       Root='HKLM' Key='Software\\GNU\\GnuPG' Name='Install Directory' />
723     </Property>
724
725     <Directory Id='TARGETDIR' Name='SourceDir'>
726       <Directory Id='ProgramFilesFolder' Name='PFiles'>
727         <Directory Id='GNU' Name='GNU'>
728           <Directory Id='INSTDIR' Name='$::INSTDIR'>
729 EOF
730
731 $::level = 12;
732 dump_all ();
733
734
735 print <<EOF;
736           </Directory>
737         </Directory>
738       </Directory>
739 EOF
740
741 if (scalar keys %::shortcuts)
742 {
743     print <<EOF;
744       <Directory Id='ProgramMenuFolder' Name='PMenu'>
745         <Directory Id='ProgramMenuDir' Name='$::name'/>
746       </Directory>
747 EOF
748 }
749
750 #print <<EOF;
751 #      <Directory Id="DesktopFolder" Name="Desktop"/>
752 #EOF
753
754
755 print <<EOF;
756     </Directory>
757
758     <Feature Id='Complete' Title='Gpg4win' Description='All components.'
759              Display='expand' Level='1' ConfigurableDirectory='INSTDIR'>
760 EOF
761
762 $::level = 6;
763 dump_all2 ();
764     
765 #    <Icon Id="Foobar10.exe" SourceFile="FoobarAppl10.exe"/>
766
767 print <<EOF;
768     </Feature>
769
770     <WixVariable Id='WixUILicenseRtf' Value='gpl.rtf'/>
771     <UIRef Id='WixUI_Mondo' />
772     <UIRef Id='WixUI_ErrorProgressText' />
773
774   </Product>
775 </Wix>
776 EOF
777
778 # Post-processing: We need to remember the GUIDs for later reuse, and
779 # we remember the files we need in case we want to transfer them to a
780 # different machine for invocation of WiX.
781
782 store_guids ();
783 store_files ();
784