[Gpg4win-commits] r598 - in trunk: . doc src
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Mon Nov 26 02:50:27 CET 2007
Author: marcus
Date: 2007-11-26 02:50:25 +0100 (Mon, 26 Nov 2007)
New Revision: 598
Modified:
trunk/ChangeLog
trunk/doc/README.de.txt
trunk/doc/README.en.txt
trunk/src/Makefile.am
trunk/src/README-msi.txt
trunk/src/gpg4win.nsi
trunk/src/inst-gpg4win.nsi
trunk/src/make-msi.bat
trunk/src/make-msi.guids
trunk/src/make-msi.pl
Log:
2007-11-26 Marcus Brinkmann <marcus at g10code.de>
* src/Makefile.am (msi, gpg4win-$(VERSION).wix,
(gpg4win-light-$(VERSION).wix): New targets.
* src/make-msi.pl: Rewritten.
* src/make-msi.guids: Include missing files.
* src/inst-gpg4win.nsi: Give section an identifier.
* src/README-msi.txt: Update.
* src/gpg4win.nsi: Do not include pinentry in the light installer.
* doc/README.de.txt, doc/README.en.txt: Document MSI package.
* src/make-msi.bat: Support other languages and light installer.
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/ChangeLog 2007-11-26 01:50:25 UTC (rev 598)
@@ -1,3 +1,15 @@
+2007-11-26 Marcus Brinkmann <marcus at g10code.de>
+
+ * src/Makefile.am (msi, gpg4win-$(VERSION).wix,
+ (gpg4win-light-$(VERSION).wix): New targets.
+ * src/make-msi.pl: Rewritten.
+ * src/make-msi.guids: Include missing files.
+ * src/inst-gpg4win.nsi: Give section an identifier.
+ * src/README-msi.txt: Update.
+ * src/gpg4win.nsi: Do not include pinentry in the light installer.
+ * doc/README.de.txt, doc/README.en.txt: Document MSI package.
+ * src/make-msi.bat: Support other languages and light installer.
+
2007-11-23 Marcus Brinkmann <marcus at g10code.de>
* packages/packages.current: Update gpgme to 1.1.6-svn1279.
Modified: trunk/doc/README.de.txt
===================================================================
--- trunk/doc/README.de.txt 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/doc/README.de.txt 2007-11-26 01:50:25 UTC (rev 598)
@@ -120,7 +120,18 @@
gpg4win.exe /S /C=C:\TEMP\gpg4win.ini /D=D:\Programme\Gpg4win
+Für den MSI installer gilt entsprechendes, mit folgenden Änderungen:
+Automatischer Ablauf wird wie üblich mit der Option /qb- zu msiexec
+erreicht. Die Steuerungsdatei muss gpg4win.ini heißen und im
+Systemverzeichnis (C:\WINDOWS) vorliegen. Der Installationspfad kann
+mit dem Eintrag "instdir" festgelegt werden. Ausserdem können die
+Einstellungen auch auf der Kommandozeile mittels INSTDIR=... und
+INST_GPA=FALSE etc. angegeben werden (die Steuerungsdatei geht
+allerdings vor). Die Angabe von Standard-Konfigurationsdateien, sowie
+die Angabe des Start Menu Verzeichnisses und die optionale Auswahl der
+Verknüpfungen wird vom MSI installer momentan nicht unterstützt.
+
5. Rechtliche Hinweise zu den einzelnen Bestandteilen der Software
==================================================================
Modified: trunk/doc/README.en.txt
===================================================================
--- trunk/doc/README.en.txt 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/doc/README.en.txt 2007-11-26 01:50:25 UTC (rev 598)
@@ -95,10 +95,23 @@
scdaemon.conf = D:\config\scdaemon-site.txt
gpa.conf = D:\config\gpa-site.conf
-
+An example command for unattended installation could look like this:
+gpg4win.exe /S /C=C:\TEMP\gpg4win.ini /D=D:\Programme\Gpg4win
+For the MSI installer, the above also holds, with the following
+changes: Unattended installation is achieved as usual with the /qb-
+option to msiexec. The control file must be called gpg4win.ini and
+reside in the system directory (C:\WINDOWS). It is found
+automatically by the installer. The installation directory can be
+specified with an entry for "instdir". Also, each entry can be given
+in uppercase at the command line through INSTDIR=... and
+INST_GPA=FALSE etc. (the control file takes precedence, though). The
+default config files as well as start menu directory and optional
+installation of short cuts are currently not supported through the MSI
+installer.
+
5. Legal notices pertaining to the individual packets
=====================================================
Modified: trunk/src/Makefile.am
===================================================================
--- trunk/src/Makefile.am 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/src/Makefile.am 2007-11-26 01:50:25 UTC (rev 598)
@@ -411,6 +411,24 @@
$(MAKENSIS) -V3 -DBUILD_DIR=`pwd` -DTOP_SRCDIR=$(top_srcdir) \
-DSRCDIR=$(srcdir) -DGPG4WIN_LIGHT=1 $(srcdir)/gpg4win.nsi
+gpg4win-$(VERSION).wix: gpg4win.nsi $(common_nsi) stamps/stamp-final \
+ gpgwrap.exe README.en.txt README.de.txt versioninfo.txt
+ perl make-msi.pl --guids $(srcdir)/make-msi.guids \
+ --manifest gpg4win-$(VERSION).files \
+ -DBUILD_DIR=. -DTOP_SRCDIR=$(top_srcdir) \
+ -DSRCDIR=$(srcdir) $(srcdir)/gpg4win.nsi > $@
+
+gpg4win-light-$(VERSION).wix: gpg4win.nsi $(common_nsi) stamps/stamp-final \
+ gpgwrap.exe README.en.txt README.de.txt versioninfo.txt
+ perl make-msi.pl --guids $(srcdir)/make-msi.guids \
+ --manifest gpg4win-light-$(VERSION).files \
+ -DBUILD_DIR=. -DTOP_SRCDIR=$(top_srcdir) \
+ -DSRCDIR=$(srcdir) -DGPG4WIN_LIGHT=1 $(srcdir)/gpg4win.nsi > $@
+
+.PHONY: msi
+msi: gpg4win-$(VERSION).wix gpg4win-light-$(VERSION).wix
+
+
stamps/stamp-dist-self: versioninfo.txt
(set -e; cd ..; make dist-bzip2)
touch stamps/stamp-dist-self
Modified: trunk/src/README-msi.txt
===================================================================
--- trunk/src/README-msi.txt 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/src/README-msi.txt 2007-11-26 01:50:25 UTC (rev 598)
@@ -28,19 +28,23 @@
$ cd gpg4win/src
-2) Run make-msi.pl to generate the required WiX source file:
+2) Create the required WiX source file:
-$ perl make-msi.pl < ../include/config.nsi > gpg4win.wix
+$ make msi
Maintainer note: The program might output the message "GUID list
stored in make-msi.guids changed, please commit!". In this case, the
-file make-msi.guids should be committed to the repository.
+modified source file make-msi.guids should be committed to the repository.
3) Now switch to the same directory on the Windows machine, and run
the script make-msi.bat to create the MSI package:
-> make-msi.bat
+> make-msi.bat gpg4win-VERSION.wix
+> make-msi.bat gpg4win-light-VERSION.wix
+where VERSION is the full version number of the build (for example,
+1.9.0-svn595).
+
The batch file assumes that WiX is installed in the canonical
location. If that is not the case, you might need to adjust the PATH
in the file.
@@ -81,11 +85,8 @@
they can be grouped in an archive using tar:
$ cd gpg4win
-$ tar -T src/make-msi.files cjf gpg4win-msi.tar.bz2
+$ tar -T src/gpg4win-VERSION.files cjf gpg4win-msi.tar.bz2
-Beside the files in gpg4win-msi.tar.bz, you also need src/gpg4win.wix
+Beside the files in gpg4win-msi.tar.bz, you also need src/gpg4win-VERSION.wix
and src/make-msi.bat on the Windows computer, which should be put into
the src/ subdirectory of the archive.
-
-
-
Modified: trunk/src/gpg4win.nsi
===================================================================
--- trunk/src/gpg4win.nsi 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/src/gpg4win.nsi 2007-11-26 01:50:25 UTC (rev 598)
@@ -51,6 +51,9 @@
!ifdef HAVE_PKG_GNUPG2
!undef HAVE_PKG_GNUPG2
!endif
+!ifdef HAVE_PKG_PINENTRY
+!undef HAVE_PKG_PINENTRY
+!endif
!ifdef HAVE_PKG_DIRMNGR
!undef HAVE_PKG_DIRMNGR
!endif
Modified: trunk/src/inst-gpg4win.nsi
===================================================================
--- trunk/src/inst-gpg4win.nsi 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/src/inst-gpg4win.nsi 2007-11-26 01:50:25 UTC (rev 598)
@@ -19,7 +19,7 @@
# This is the very first section installed.
-Section "-gpg4win"
+Section "-gpg4win" SEC_gpg4win
!ifdef SOURCES
SetOutPath "$INSTDIR"
File "${BUILD_DIR}/../gpg4win-${VERSION}.tar.bz2"
Modified: trunk/src/make-msi.bat
===================================================================
--- trunk/src/make-msi.bat 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/src/make-msi.bat 2007-11-26 01:50:25 UTC (rev 598)
@@ -1,4 +1,30 @@
+REM Usage: make-msi.bat [-L{de|en}] [FILE]
+REM Defaults: -Len gpg4win.wix
+REM
set WIXPATH=C:\"Program Files"\"Windows Installer XML v3"\bin
-%WIXPATH%\candle.exe gpg4win.wix
-%WIXPATH%\light.exe -ext WixUIExtension -cultures:en-us gpg4win.wixobj
+set LANG=en-us
+IF NOT "%1"=="-Lde" GOTO langde
+ shift
+ set LANG=de-de
+ GOTO langset
+:langde
+IF NOT "%1"=="-Len" GOTO langen
+ shift
+ set LANG=en-us
+ GOTO langset
+:langen
+
+:langset
+
+
+set FILE=gpg4win.wix
+IF "%1"=="" GOTO nofile
+ set FILE=%1
+:nofile
+
+%WIXPATH%\candle.exe %FILE%
+%WIXPATH%\light.exe -ext WixUIExtension -cultures:%LANG% %FILE%obj
+
+%WIXPATH%\candle.exe %FILE%
+%WIXPATH%\light.exe -ext WixUIExtension -cultures:%LANG% %FILE%obj
Modified: trunk/src/make-msi.guids
===================================================================
--- trunk/src/make-msi.guids 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/src/make-msi.guids 2007-11-26 01:50:25 UTC (rev 598)
@@ -1,6 +1,7 @@
# This is an automatically generated file. DO NOT EDIT.
c98dadcc-b796-41cf-aff2-bfa1706a5a6f /PRODUCT/1.9.0.581
06b9c50c-352a-4fe2-9f47-4f6348d9dda4 /PRODUCT/1.9.0.587
+b3ce3cd9-0436-4348-8c09-106163ad9170 /PRODUCT/1.9.0.595
fb54db39-2456-4fcc-9550-2790db663015 /REGISTRY/HKLM/Software\GNU\GnuPG/Install Directory
ad23691b-1734-4dec-b91a-d4ff286190ea /REGISTRY/HKLM/Software\GNU\GnuPG/gpgProgram
e0807a2f-4e70-48f8-b047-c5d3e219c3e7 /UPGRADE/1
@@ -253,9 +254,12 @@
01020ce6-6c8c-460f-8f66-a8f734fa3fec share\gpa\gpa_logo.ppm
fa992dcc-ff89-4a32-9723-0b76bbc1dae4 share\gpa\gpa_tips.de
1e855765-7624-42bb-b06b-28a0bcfce29b share\gpa\gpa_tips.en
+9433746e-dddf-421c-9dd0-f5b1cefaadb7 share\gpg4win\README.de.txt
+13b17315-1137-401c-8021-020cc0e5db70 share\gpg4win\README.en.txt
592ab2a3-a681-4baf-80ff-9d8d9ea3ce95 share\gpg4win\durchblicker.pdf
b1b23af2-9aa8-4055-9ff8-6e0d26c8dd0e share\gpg4win\einsteiger.pdf
806964aa-cb2a-4d5e-b53c-9525736563f2 share\gpg4win\novices.pdf
+85a68157-9931-4a40-879c-b3f24361930f share\gpg4win\versioninfo.txt
4ca6e2ae-6987-4a21-9268-dc652b709fe2 share\icons\oxygen\16x16\actions\1day.png
f244617c-8b96-4f99-aa69-d73a41c287fd share\icons\oxygen\16x16\actions\5days.png
d38f25e8-1245-4687-bf17-4e5be87a548a share\icons\oxygen\16x16\actions\7days.png
Modified: trunk/src/make-msi.pl
===================================================================
--- trunk/src/make-msi.pl 2007-11-23 17:40:56 UTC (rev 597)
+++ trunk/src/make-msi.pl 2007-11-26 01:50:25 UTC (rev 598)
@@ -18,61 +18,36 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
-# Invoke like this:
+# TODO
+# ====
#
-# perl make-msi.pl < ../include/config.nsi
+# 1. The UI extension in WiX 3.0 only supports en-us.
+# Translation to german is desired.
#
-# Note that this needs to be called from the gpg4win src/ directory, because
-# a number of files (inst-*.nsi for example) are accessed in that directory.
+# 1. Edit license dialog to not require acceptance (see tutorial, lesson 2).
#
-# This program parses the NSIS source files and creates a .wix file,
-# which needs to be compiled with candle and linked with light, see
-# make-msi.bat.
+# 2. Support .ini file for customization.
#
-# The file make-msi.guids is read and updated. It contains GUIDs for all
-# components used by the installer, which are kept from version to version.
-#
-# Also, the output file make-msi.files contains a list of all files
-# that will be accessed by the linker when creating the package.
+# 3. Add README dialog and launch README file:
+# <Property Id='NOTEPAD'>Notepad.exe</Property>
+# <CustomAction Id='LaunchFile' Property='NOTEPAD' ExeCommand='[SourceDir]Readme.txt' Return='asyncNoWait'/>
+# Problems: Dialog is missing. How to select language to display?
use strict;
+use warnings;
+use diagnostics;
-# TODO:
-#
-# DirMngr config files/cache directory? service start fails!!!
-# desktop and quick launch entries, but optional (also startmenu optional)
-
-# The list of all enabled packages.
-@::pkgs = ();
-# All definitions from config.nsi (the input file)
-%::config = ();
-# A description of the components. We have one component per package.
-# Creating the data for these components is most of the work.
-@::components = ();
-# A list of all source files included in the package.
-@::sources = ();
-# A hash which maps frobbed package names to a hash of frobbed package
-# names on which they depend.
-%::deps = ();
-# A hash which contains one key for each file that wants a shortcut in the
-# canonical places (start menu, desktop, quick launch).
-%::shortcuts = ();
-
-$::INSTDIR = 'GnuPG';
-$::name = 'GnuPG for Windows';
-
-# Simple indentation tracking, for pretty printing.
-$::level = 0;
-
-# FIXME: Some work arounds for the manual.
+# Default language.
+$::lang = 'en';
-my $DESC_Name_man_advanced_de = "Advanced Manual (German)";
-my $DESC_Name_man_advanced_en = "Advanced Manual";
-my $DESC_Name_man_novice_de = "Novice Manual (German)";
-my $DESC_Name_man_novice_en = "Novice Manual";
-
+sub fail
+{
+ print STDERR $_[0] . "\n";
+ exit 1;
+}
+
# We use a new product and package code for every build (using pseudo
# components), but a single constant upgrade code for all versions.
# Note that Windows installer ignores the build part of the version
@@ -140,15 +115,20 @@
}
-$::files_file = 'make-msi.files';
+$::files_file = '';
# We store the list of included files for temporary packaging, in case
# WiX needs to be run on a different system.
sub store_files
{
+ my ($parser) = @_;
+
+ return if ($::files_file eq '');
open (FILE, ">$::files_file") or die;
- foreach my $pkg (@::components)
+ foreach my $name (@{$parser->{pkg_list}})
{
+ my $pkg = $parser->{pkgs}->{$name};
+
next if ($#{$pkg->{files}} == -1);
print FILE (join ("\n", map { "src/" . ($_->{source}) }
@{$pkg->{files}})). "\n";
@@ -157,248 +137,880 @@
}
-sub get_deps
+sub lang_to_lcid
{
- my $name = '';
- my %deps = ();
+ my ($lang) = @_;
- # FIXME: Check if file exists.
- open (FILE, "<inst-sections.nsi") or return;
- while (<FILE>)
+ if ($lang eq 'en')
{
- my $line = $_;
+ return 1033;
+ }
+ elsif ($lang eq 'de')
+ {
+ return 1031;
+ }
+ else
+ {
+ fail "language $lang not supported";
+ }
+}
+
+
+# NSIS parser
- if ($name eq '')
+# The parser data structure contains the following members:
+#
+# pre_depth: The current nesting depth of preprocessor conditionals.
+# pre_true: Depth of the last preprocessor conditional that was true.
+# pre_symbols: A hash of defined preprocessor symbols.
+# po: A hash of languages, each a hash of translated strings.
+# outpath: the current output path.
+# includedirs: An array of include directories to search through.
+
+# A couple of variables you can set:
+$::nsis_parser_warn = 0;
+$::nsis_parser_debug = 0;
+
+$::nsis_level_default = 1;
+$::nsis_level_optional = 1000;
+$::nsis_level_hidden = 2000;
+
+# Evaluate an expression.
+sub nsis_eval
+{
+ my ($parser, $file, $expr) = @_;
+ my $val = $expr;
+
+ # Resolve outer double quotes, if any.
+ if ($val =~ m/^"/)
+ {
+ if (not $val =~ s/^"(.*)"$/$1/)
{
- if ($line =~ m/^\s*have_(\S+):\s*\r?\n$/)
+ fail "$file:$.: unmatched quote in expression: $expr";
+ }
+ }
+
+ my $iter = 0;
+ while ($val =~ m/\${([^}]*)}/)
+ {
+ my $varname = $1;
+ my $varvalue;
+
+ if (exists $parser->{pre_symbols}->{$varname})
+ {
+ $varvalue = $parser->{pre_symbols}->{$varname};
+ }
+ else
+ {
+ fail "$file:$.: undefined variable $varname in expression: $expr";
+ }
+ $val =~ s/\${$varname}/$varvalue/g;
+
+ $iter++;
+ if ($iter > 100)
+ {
+ fail "$file:$.: too many variable expansions in expression: $expr";
+ }
+ }
+
+# # FIXME: For now.
+# if ($expr =~ m/\$/ or $expr !~ m/^\"/)
+# {
+# return $expr;
+# }
+# $val = eval $expr;
+ return $val;
+}
+
+
+# Retrieve an evaluated symbol
+sub nsis_fetch
+{
+ my ($parser, $symname) = @_;
+
+ return undef if (not exists $parser->{pre_symbols}->{$symname});
+
+ return nsis_eval ($parser, '', $parser->{pre_symbols}->{$symname});
+}
+
+
+# Evaluate an expression.
+sub nsis_translate
+{
+ my ($parser, $file, $expr) = @_;
+ my $val = $expr;
+
+ # Resolve outer double quotes, if any.
+ if ($val =~ m/^\$\((.*)\)$/)
+ {
+ if (exists $parser->{po}->{$::lang}->{$1})
+ {
+ $val = $parser->{po}->{$::lang}->{$1};
+ }
+ else
+ {
+ fail "$file:$.: no translation for $val to language $::lang";
+ }
+ }
+
+ $val =~ s/^"(.*)"$/$1/;
+ $val =~ s/\$\r/\r/g;
+ $val =~ s/\$\n/\n/g;
+ $val =~ s/\$\"/"/g;
+
+ return $val;
+}
+
+
+# Low level line input.
+sub nsis_get_line
+{
+ my ($file) = @_;
+ my $line = '';
+
+ while (<$file>)
+ {
+ $line = $line . $_;
+
+ # Strip leading whitespace.
+ $line =~ s/^\s*//;
+
+ # Strip newline and trailing whitespace.
+ $line =~ s/\s*\r?\n$//;
+
+ # Combine multiple lines connected with backslashes.
+ if ($line =~ m/^(.*)\\$/)
+ {
+ $line = $1 . ' ';
+ next;
+ }
+
+ $_ = $line;
+ last;
+ }
+
+ # Now break up the line into
+ return $_;
+}
+
+
+# Tokenize the NSIS line.
+sub nsis_tokenize
+{
+ my ($file, $line) = @_;
+ my @tokens;
+
+ my @line = split ('', $line);
+ my $idx = 0;
+
+ while ($idx <= $#line)
+ {
+ # The beginning of the current partial token.
+ my $token = $idx;
+
+ if ($line[$idx] eq '"')
+ {
+ $idx++;
+ # Skip until end of string, indicated by double quote that
+ # is not part of the $\" string.
+ while ($idx <= $#line)
{
- $name = $1;
+ if (substr ($line, $idx, 3) eq '$\\"')
+ {
+ $idx += 3;
+ }
+ else
+ {
+ last if ($line[$idx] eq '"');
+ $idx++;
+ }
}
+ fail "$file:$.:$idx: unterminated string from position $token"
+ if ($idx > $#line);
+ $idx++;
+ fail "$file:$.:$idx: strings not separated"
+ if ($idx <= $#line and $line[$idx] !~ m/\s/);
}
+ elsif ($line[$idx] eq '\'')
+ {
+ $idx++;
+ # Skip until end of string, indicated by a single quote.
+ while ($idx <= $#line)
+ {
+ last if ($line[$idx] eq '\'');
+ $idx++;
+ }
+ fail "$file:$.:$idx: unterminated string from position $token"
+ if ($idx > $#line);
+ $idx++;
+ fail "$file:$.:$idx: strings not separated"
+ if ($idx <= $#line and $line[$idx] !~ m/\s/);
+ }
else
{
- if ($line =~ m/^\s*!insertmacro\s+SelectSection\s+\$\{SEC_(\S+)\}\s*\r?\n$/)
+ # Skip until end of token indicated by whitespace.
+ while ($idx <= $#line)
{
- $deps{$1} = 1;
+ fail "$file:$.:$idx: invalid character"
+ if ($line[$idx] eq '"');
+
+ last if ($line[$idx] =~ m/\s/);
+ $idx++;
}
- elsif ($line =~ m/^\s*skip_$name:\s*\r?\n$/)
+ }
+
+ push @tokens, substr ($line, $token, $idx - $token);
+
+ # Skip white space between arguments.
+ while ($idx <= $#line and $line[$idx] =~ m/\s/)
+ {
+ $idx++;
+ }
+ }
+
+ return @tokens;
+}
+
+
+# We suppress some warnings after first time.
+%::warn = ();
+
+# Parse the NSIS line.
+sub nsis_parse_line
+{
+ my ($parser, $file, $line) = @_;
+
+ # We first tokenize the line.
+ my @tokens = nsis_tokenize ($file, $line);
+
+ # We handle preprocessing directives here.
+
+ print STDERR "Tokens: " . join (" AND ", @tokens) . "\n"
+ if $::nsis_parser_debug;
+
+ # We have special code dealing with ignored areas.
+ if ($parser->{pre_depth} > $parser->{pre_true})
+ {
+ if ($tokens[0] eq '!ifdef' or $tokens[0] eq '!ifndef')
+ {
+ fail "$file:$.: syntax error" if $#tokens != 1;
+ $parser->{pre_depth}++;
+ }
+ elsif ($tokens[0] eq '!else')
+ {
+ fail "$file:$.: stray !else" if $parser->{pre_depth} == 0;
+
+ if ($parser->{pre_depth} == $parser->{pre_true} + 1)
{
- # We resolve indirect dependencies right now.
- foreach my $pkg (keys %::deps)
+ $parser->{pre_true}++;
+ }
+ }
+ elsif ($tokens[0] eq '!endif')
+ {
+ fail "$file:$.: syntax error" if $#tokens != 0;
+
+ fail "$file:$.: stray !endif" if $parser->{pre_depth} == 0;
+
+ $parser->{pre_depth}--;
+ }
+ elsif ($tokens[0] eq '!macro')
+ {
+ fail "$file:$.: syntax error" if $#tokens < 1;
+
+ # FIXME: We do not support macros at this point, although
+ # support would not be too hard to add. Instead, we just
+ # ignore their definition so it does not throw us off.
+
+ print STDERR
+ "$file:$.: warning: ignoring macro $tokens[1]\n"
+ if $::nsis_parser_warn;
+
+ $parser->{pre_depth}++;
+ }
+ elsif ($tokens[0] eq '!macroend')
+ {
+ # FIXME: See !macro.
+ fail "$file:$.: stray !macroend" if $parser->{pre_depth} == 0;
+ $parser->{pre_depth}--;
+ }
+ }
+ else
+ {
+ # This is the parser for areas not ignored.
+ if ($tokens[0] eq '!define')
+ {
+ if ($#tokens == 1)
+ {
+ # FIXME: Maybe define to 1?
+ $parser->{pre_symbols}->{$tokens[1]} = '';
+ }
+ elsif ($#tokens == 2)
+ {
+ $parser->{pre_symbols}->{$tokens[1]} =
+ nsis_eval ($parser, $file, $tokens[2]);
+ }
+ else
+ {
+ fail "$file:$.: syntax error";
+ }
+
+ }
+ elsif ($tokens[0] eq '!undef')
+ {
+ fail "$file:$.: syntax error" if $#tokens != 1;
+ delete $parser->{pre_symbols}->{$tokens[1]};
+ }
+ elsif ($tokens[0] eq '!ifdef')
+ {
+ fail "$file:$.: syntax error" if $#tokens != 1;
+
+ if (exists $parser->{pre_symbols}->{$tokens[1]})
+ {
+ $parser->{pre_true}++;
+ }
+ $parser->{pre_depth}++;
+ }
+ elsif ($tokens[0] eq '!ifndef')
+ {
+ fail "$file:$.: syntax error" if $#tokens != 1;
+
+ if (not exists $parser->{pre_symbols}->{$tokens[1]})
+ {
+ $parser->{pre_true}++;
+ }
+ $parser->{pre_depth}++;
+ }
+ elsif ($tokens[0] eq '!else')
+ {
+ fail "$file:$.: stray !else" if $parser->{pre_depth} == 0;
+
+ if ($parser->{pre_depth} == $parser->{pre_true})
+ {
+ $parser->{pre_true}--;
+ }
+ elsif ($parser->{pre_depth} == $parser->{pre_true} + 1)
+ {
+ $parser->{pre_true}++;
+ }
+ }
+ elsif ($tokens[0] eq '!endif')
+ {
+ fail "$file:$.: syntax error" if $#tokens != 0;
+
+ fail "$file:$.: stray !endif" if $parser->{pre_depth} == 0;
+
+ if ($parser->{pre_depth} == $parser->{pre_true})
+ {
+ $parser->{pre_true}--;
+ }
+ $parser->{pre_depth}--;
+ }
+ elsif ($tokens[0] eq '!include')
+ {
+ fail "$file:$.: syntax error" if $#tokens != 1;
+
+ print STDERR "Including $tokens[1]\n"
+ if $::nsis_parser_debug;
+
+ my $filename = nsis_eval ($parser, $file, $tokens[1]);
+
+ # Recursion.
+ nsis_parse_file ($parser, $filename);
+ }
+ elsif ($tokens[0] eq '!macro')
+ {
+ fail "$file:$.: syntax error" if $#tokens < 1;
+
+ # FIXME: We do not support macros at this point, although
+ # support would not be too hard to add. Instead, we just
+ # ignore their definition so it does not throw us off.
+
+ print STDERR
+ "$file:$.: warning: ignoring macro $tokens[1]\n"
+ if $::nsis_parser_warn;
+
+ $parser->{pre_depth}++;
+ }
+ elsif ($tokens[0] eq '!macroend')
+ {
+ # FIXME: See !macro.
+ fail "$file:$.: stray !macroend" if $parser->{pre_depth} == 0;
+ $parser->{pre_depth}--;
+ }
+ elsif ($tokens[0] eq '!cd' or $tokens[0] eq '!addplugindir')
+ {
+ if (not exists $::warn{"directive-$tokens[0]"})
+ {
+ print STDERR
+ "$file:$.: warning: ignoring $tokens[0] directive\n"
+ if $::nsis_parser_warn;
+ }
+ $::warn{"directive-$tokens[0]"}++;
+ }
+ elsif ($tokens[0] eq '!addincludedir')
+ {
+ fail "$file:$.: syntax error" if $#tokens != 1;
+
+ my $dir = nsis_eval ($parser, $file, $tokens[1]);
+
+ unshift @{$parser->{includedirs}}, $dir;
+ }
+ elsif ($tokens[0] =~ m/^\!/ and $tokens[0] ne '!insertmacro')
+ {
+ # Note: It is essential that some !insertmacro invocations are
+ # not expanded, namely those of SelectSection and UnselectSection,
+ # which are used to track dependencies in Gpg4win.
+
+ fail "$file:$.: compiler directive $tokens[0] not implemented";
+ }
+ else
+ {
+ # Main processing routine. This is specific to the backend
+ # and probably package.
+ gpg4win_nsis_stubs ($parser, $file, @tokens);
+ }
+ }
+}
+
+
+# Parse the NSIS file.
+sub nsis_parse_file
+{
+ my ($parser, $file) = @_;
+ my $handle;
+
+ if ($file eq '-')
+ {
+ $. = 0;
+ $handle = *STDIN;
+ }
+ else
+ {
+ if (not -e $file and 1)
+ {
+ # Search for include file. Note: We do not change
+ # directories, but that is OK for us. Also, we want to
+ # avoid the system header files, as we don't control what
+ # constructs they use, and in fact we want to treat their
+ # macros and functions as atoms.
+
+ my @includedirs = @{$parser->{includedirs}};
+ my $dir;
+
+ foreach $dir (@includedirs)
+ {
+ if (-e $dir . '/' . $file)
{
- if (defined $::deps{$pkg}->{$name})
- {
- foreach my $dep (keys %deps)
- {
- $::deps{$pkg}->{$dep} = $::deps{$pkg}->{$name} + 1
- if (not defined $::deps{$pkg}->{$dep})
- }
- }
+ $file = $dir . '/' . $file;
+ last;
}
- $::deps{$name} = { %deps };
- $name = '';
- %deps = ();
}
}
+
+ if (not open ($handle, "<$file"))
+ {
+ print STDERR "$file:$.: warning: "
+ . "can not open include file $file: $!\n"
+ if $::nsis_parser_warn;
+ return;
+ }
}
- close (FILE);
+
+ while (defined nsis_get_line ($handle))
+ {
+ $.++ if ($file eq '-');
+
+ # Skip comment lines.
+ next if $_ =~ m/^#/;
+
+ # Skip empty lines.
+ next if $_ =~ m/^$/;
+
+ nsis_parse_line ($parser, $file, $_);
+ }
+
+ close $handle if ($file ne '-');
}
-sub get_shortcuts
+# The Gpg4win stubs for the MSI backend to the NSIS converter.
+
+# Gpg4win specific state in $parser:
+# pkg: the current package (a hash reference), corresponds to certain sections.
+# pkgs: a hash ref of all packages encountered indexed by their frobbed name.
+# pkg_list: the order of packages (as frobbed names).
+# state: specifies a state for special parsing of certain parts.
+# dep_name: the current package for which we list dependencies (- for none)
+
+sub gpg4win_nsis_stubs
{
- my %shortcuts = ();
+ my ($parser, $file, $command, @args) = @_;
- # Pending line.
- my $line;
+ $parser->{state} = "" if not defined $parser->{state};
+
+ if ($parser->{state} =~ m/^ignore-until-(.*)$/)
+ {
+ undef $parser->{state} if ($command eq $1);
+ }
- # FIXME: Check if file exists.
- open (FILE, "<inst-sections.nsi") or return;
- while (<FILE>)
+ # Section support.
+ #
+ # We parse SetOutPath and File directives in sections.
+ # Everything else is ignored.
+
+ elsif ($parser->{state} eq '' and $command eq 'Section')
{
- # Combine multiple lines connected with backslashes.
- $line = $line . $_;
- if ($line =~ m/^(.*)\\\s*\r?\n$/)
+ my $idx = 0;
+ # Default install level for MSI is 3.
+ my $level = $::nsis_level_default;
+ my $hidden = 0;
+
+ # Check for options first.
+ return if ($idx > $#args);
+ if ($args[$idx] eq '/o')
{
- $line = $1 . ' ';
- next;
+ # Default install level for MSI is 3.
+ $level = $::nsis_level_optional;
+ $idx++;
}
- $_ = $line;
- $line = '';
- if (m,^\s*CreateShortCut\s+\"\$SMPROGRAMS\\\$STARTMENU_FOLDER\\[^.]+\.lnk\"\s+\"\$INSTDIR\\([^"]+)\",)
+ return if ($idx > $#args);
+
+ my $title = nsis_eval ($parser, $file, $args[$idx++]);
+
+ # Check for hidden flag.
+ if (substr ($title, 0, 1) eq '-')
{
- $shortcuts{$1} = 1;
+ # Hidden packages are dependency tracked and never
+ # installed by default unless required.
+ $level = $::nsis_level_hidden;
+ $hidden = 1;
+ substr ($title, 0, 1) = '';
}
+
+ # We only pay attention to special sections and those which
+ # have a section index defined.
+ if ($title eq 'startmenu')
+ {
+ # The special startmenu section contains all our shortcuts.\
+ $parser->{state} = 'section-startmenu';
+ return;
+ }
+ elsif ($idx > $#args)
+ {
+ return;
+ }
+
+ # Finally we can get the frobbed name of the package.
+ my $name = $args[$idx++];
+ $name =~ s/^SEC_//;
+
+ my $pkg = \%{$parser->{pkgs}->{$name}};
+
+ $pkg->{name} = $name;
+ $pkg->{title} = $title;
+ $pkg->{level} = $level;
+ $pkg->{hidden} = $hidden;
+ $pkg->{features} = '';
+
+ # Remember the order of sections included.
+ push @{$parser->{pkg_list}}, $name;
+
+ $parser->{pkg} = $pkg;
+ $parser->{state} = 'in-section';
}
- close (FILE);
- %::shortcuts = %shortcuts;
-}
+ elsif ($parser->{state} eq 'in-section')
+ {
+ if ($command eq 'SectionEnd')
+ {
+ delete $parser->{pkg};
+ undef $parser->{state};
+ }
+ elsif ($command eq 'SetOutPath')
+ {
+ fail "$file:$.: syntax error" if ($#args != 0);
-
-sub collect_all
-{
- # Input file is $(top_srcdir)/include/config.nsi
+ my $outpath = $args[0];
+ if (not $outpath =~ s/^"\$INSTDIR\\?(.*)"$/$1/)
+ {
+ fail "$file:$.: unsupported out path: $args[0]";
+ }
+ $parser->{outpath} = $outpath;
+ }
+ elsif ($command eq 'File')
+ {
+ my $idx = 0;
+ my $target;
+
+ fail "$file:$.: not supported" if ($#args < 0 || $#args > 1);
+
+ if ($#args == 1)
+ {
+ if ($args[0] eq '/nonfatal')
+ {
+ print STDERR "$file:$.: warning: skipping non-fatal file $args[1]\n"
+ if $::nsis_parser_warn;
+ return;
+ }
+
+ $target = $args[0];
+ if (not $target =~ s,^/oname=(.*)$,$1,)
+ {
+ fail "$file:$.: syntax error";
+ }
+
+ # Temp files are due to overwrite attempts, which are
+ # handled automatically by the Windows Installer. Ignore
+ # them here.
+ return if $target =~ m/\.tmp$/;
+ $idx++;
+ }
+
+ my $source = nsis_eval ($parser, $file, $args[$idx]);
+ if (not defined $target)
+ {
+ $target = $source;
+ $target =~ s,^.*/([^/\\]+)$,$1,;
+ }
- while (<>)
- {
- if (/^!define\s+(\w+)\s+(\S.*\S)\s*\r?\n$/)
- {
- $::config{$1} = $2;
- }
- }
- # gpg4win_build_list is a C-like string, so strip the quotes first.
- my $pkg_list;
- eval '$pkg_list = ' . $::config{gpg4win_build_list};
- @::pkgs = split (' ', $pkg_list);
+ push @{$parser->{pkg}->{files}}, { source => $source,
+ dir => $parser->{outpath},
+ target => $target };
+ }
+ elsif ($command eq 'WriteRegStr')
+ {
+ fail "$file:$.: not supported" if ($#args != 3);
- # Now we have a list of packages to process. For each package,
- # create a nice data structure that captures all the information we
- # collect.
+ my $root = $args[0];
- foreach my $pkg (@::pkgs)
- {
- my %pkg;
- $pkg{name} = $pkg;
- $pkg =~ tr/-+/__/;
- $pkg{frobbed_name} = $pkg;
- $pkg{version} = $::config{"gpg4win_pkg_${pkg}_version"};
- $pkg{source} = $::config{"gpg4win_pkg_${pkg}"};
- $pkg{features} = '';
- $pkg{hidden} = 0;
+ my $key = $args[1];
+ $key =~ s/^"(.*)"$/$1/;
- # We parse the inst-package file to figure out what to do. This
- # is not a full-featured NSIS to MSI converter, but it does the
- # job for us.
+ my $name = $args[2];
+ $name =~ s/^"(.*)"$/$1/;
- my $prefix;
- if (defined $pkg{version})
- {
- $prefix = "playground/install/pkgs/$pkg{name}-$pkg{version}";
- }
+ my $value = $args[3];
+ $value =~ s/^"(.*)"$/$1/;
+ $value =~ s/\$INSTDIR\\?/\[INSTDIR\]/g;
- # The list of all files encountered and included in the package.
- my @files;
- # The list of all registry settings to write.
- my @registry;
+ push (@{$parser->{pkg}->{registry}},
+ { root => $root, key => $key, name => $name,
+ value => $value, type => 'string' });
+ }
+ }
- # The current directory.
- my $dir = '';
+ # Start menu shortcuts support.
- # The pending line.
- my $line = '';
+ elsif ($parser->{state} eq 'section-startmenu')
+ {
+ if ($command eq 'SectionEnd')
+ {
+ undef $parser->{state};
+ }
+ elsif ($command eq 'CreateShortCut')
+ {
+ fail "$file:$.: not supported" if ($#args != 7);
- open (FILE, "<inst-$pkg{name}.nsi") or die;
- while (<FILE>)
- {
- # Combine multiple lines connected with backslashes.
- $line = $line . $_;
- if ($line =~ m/^(.*)\\\s*\r?\n$/)
- {
- $line = $1 . ' ';
- next;
- }
- $_ = $line;
- $line = '';
+ # The link may contains a translatable string.
+ my $link = $args[0];
- # FIXME: Handle hidden packages "-foo".
- if (m,^\s*Section\s+"-([^"]+)",)
- {
- # Hidden packages are dependency-tracked.
- $pkg{title} = $1;
- $pkg{level} = 2000; # Superfluous.
- $pkg{hidden} = 1;
- }
- elsif (m,^\s*Section\s+"([^"]+)",)
- {
- # FIXME: Work around for manuals, which have variables
- # in this place.
- my $title = $1;
- $title =~ s/^\$\((.*)\)$/\$$1/;
- eval '$pkg{title} = "' . $title . '"';
- $pkg{level} = 1;
- }
- elsif (m,^\s*Section\s+/o\s+"([^"]+)",)
- {
- # FIXME: Work around for manuals, which have variables
- # in this place.
- $pkg{title} = $1;
- # Default install level is 3.
- $pkg{level} = 1000;
- }
- elsif (m,^\s*LangString\s+DESC_SEC_\S+\s+\$\{LANG_ENGLISH\}\s+\"([^"]+)\"\s*\r?\n,)
- {
- $pkg{description} = $1;
- }
- # Special hack for kdesupport.nsi. FIXME: Could do real
- # variable substitution here.
- elsif (m,^\s*\!define prefix \${ipdir}/([^\$]+)-\$.*\r?\n$,)
- {
- $prefix = "playground/install/pkgs/$1-$pkg{version}";
- }
- elsif (m,^\s*SetOutPath\s+"?\$INSTDIR\\?([^"]*)"?\s*\r?\n$,)
- {
- $dir = $1;
- }
- elsif (m,^\s*File\s+"?\$\{(prefix|BUILD_DIR|SRCDIR)\}(?:/(\S*))?/([^/"\s]+)"?\s*\r?\n$,)
- {
- my $source = $3;
+ # We filter for startmenu shortcuts, as the others are
+ # just more of the same. Equivalently, we could filter
+ # for a block between two labels.
+ return if ($link !~ m/STARTMENU_FOLDER/);
- $source = "$2/$source" if defined $2;
- $source = "${prefix}/$source" if $1 eq 'prefix';
- # FIXME: We assume that srcdir == build_dir here.
+ # Take the base name of the link. */
+ $link =~ s/^.*\\([^\\]*)\"$/$1/;
- push @files, { source => $source, dir => $dir, target => $3 };
- push @::sources, $source;
- }
- elsif (m,^\s*File\s+/oname=(\S+)\s+"?\$\{(prefix|BUILD_DIR)\}/([^"\s]+)"?\s*\r?\n$,)
- {
- my $target = $1;
- my $source = $3;
+ my $target = nsis_eval ($parser, $file, $args[1]);
+ $target =~ s/^\$INSTDIR\\//;
- $source = "${prefix}/$source" if $2 eq 'prefix';
+ my $icon = nsis_eval ($parser, $file, $args[3]);
+ my $icon_idx = nsis_eval ($parser, $file, $args[4]);
+ fail "$file:$.: not supported" if ($icon_idx ne '');
- # Temp files are due to overwrite attempts, which are
- # handled automatically by the Windows Installer.
- # Ignore them here.
- next if $target =~ m/\.tmp$/;
+ # The description contains a translatable string.
+ my $description = $args[7];
- push @files, { source => $source,
- dir => $dir, target => $target };
- push @::sources, $source;
- }
- elsif (m,^\s*WriteRegStr\s+(\S+)\s+"([^"]+)"\s+"([^"]+)"\s+"?([^"]+)"?\s*\r?\n$,)
- {
- my ($root, $key, $name, $value) = ($1, $2, $3, $4);
- $value =~ s/\$INSTDIR/\[INSTDIR\]/g;
- push (@registry,
- { root => $root, key => $key, name => $name,
- value => $value, type => 'string' });
- }
- }
- close (FILE);
- $pkg{files} = \@files;
- $pkg{registry} = \@registry;
+ $parser->{shortcuts}->{$target} = { link => $link,
+ target => $target,
+ icon => $icon,
+ description => $description };
+ }
+ }
- # Some things we can not easily parse from the NSI files. For
- # these, we do manual overrides here.
- if ($pkg{name} eq 'gnupg')
- {
- $pkg{features} .= " Absent='disallow'";
- }
- elsif ($pkg{name} eq 'gnupg2')
- {
- $pkg{features} .= " Absent='disallow'";
- }
+ # LangString support.
+ #
+ # LangString directives must be stated at the top-level of the file.
- push @::components, \%pkg;
- }
+ elsif ($parser->{state} eq '' and $command eq 'LangString')
+ {
+ fail "$file:$.: syntax error" if ($#args != 2);
+
+ my $lang = $args[1];
+ $lang =~ s/^\$\{LANG_(\w*)\}$/$1/;
+ if ($lang eq 'ENGLISH')
+ {
+ $lang = 'en';
+ }
+ elsif ($lang eq 'GERMAN')
+ {
+ $lang = 'de';
+ }
+ else
+ {
+ fail "$file:$.: unsupported language ID $args[1]";
+ }
+ $parser->{po}->{$lang}->{$args[0]} = $args[2];
+ }
+
+ # Function support.
+ #
+ # Most functions are ignored. Some are of special interest and
+ # are parsed separately.
+
+ elsif ($parser->{state} eq '' and $command eq 'Function')
+ {
+ fail "$file:$.: syntax error" if ($#args != 0);
+
+ if ($args[0] eq 'CalcDepends')
+ {
+ $parser->{state} = 'function-calc-depends';
+ }
+ elsif ($args[0] eq 'CalcDefaults')
+ {
+ $parser->{state} = 'function-calc-defaults';
+ }
+ else
+ {
+ # Functions we do not find interesting are skipped.
+ print STDERR
+ "$file:$.: warning: ignoring function $args[0]\n"
+ if $::nsis_parser_warn;
+ delete $parser->{dep_name};
+ $parser->{state} = 'ignore-until-FunctionEnd';
+ }
+ }
+
+ # Function calc-depends.
+ #
+ # This function gathers information about dependencies between
+ # features. Features are identified by their frobbed names. The
+ # format is as such: First, a couple of UnselectSection macros,
+ # one for each dependency. Then SelectSection invocations for all
+ # packages which should always be installed (mandatory), followed
+ # by one block for each feature, consisting of a label "have_FOO:"
+ # where FOO is the frobbed package name (in lowercase, usually),
+ # followed by SelectSection invocations, one for each dependency,
+ # and finally a "skip_FOO:" label to finish the block.
+ #
+ # The order of these statements and blocks must be so that a single pass
+ # through the list is sufficient to resolve all dependencies, that means
+ # in pre-fix order.
+
+ elsif ($parser->{state} eq 'function-calc-depends')
+ {
+ if ($command eq 'FunctionEnd')
+ {
+ undef $parser->{state};
+ }
+ elsif ($command =~ m/^have_(.*):$/)
+ {
+ $parser->{dep_name} = $1;
+ $parser->{pkgs}->{$1}->{deps} = {};
+ }
+ elsif ($command eq '!insertmacro')
+ {
+ fail "$file:$.: syntax error" if $#args < 0;
+ if ($args[0] eq 'SelectSection')
+ {
+ fail "$file:$.: syntax error" if $#args != 1;
+ my $name = $args[1];
+ $name =~ s/^\$\{SEC_(.*)\}$/$1/;
+
+ if (not exists $parser->{dep_name})
+ {
+ # A stray SelectSection chooses defaults.
+ $parser->{pkgs}->{$name}->{features} .=
+ " Absent='disallow'";
+ }
+ else
+ {
+ my $dep_name = $parser->{dep_name};
+
+ # Add $name as a dependency for $dep_name.
+ $parser->{pkgs}->{$dep_name}->{deps}->{$name} = 1;
+ }
+ }
+ }
+ elsif ($command =~ m/^skip_(.*):$/)
+ {
+ fail "$file:$.: stray skip_FOO label"
+ if not exists $parser->{dep_name};
+
+ my $dep_name = $parser->{dep_name};
+ my $dep_pkg = $parser->{pkgs}->{$dep_name};
+
+ # We resolve indirect dependencies right now. This works
+ # because dependencies are required to be listed in
+ # pre-fix order.
+
+ foreach my $name (keys %{$parser->{pkgs}})
+ {
+ my $pkg = $parser->{pkgs}->{$name};
+
+ # Check if $dep_name is a dependency for $name.
+ if (exists $pkg->{deps}->{$dep_name})
+ {
+ # Add all dependencies of $dep_name to $name.
+ foreach my $dep (keys %{$dep_pkg->{deps}})
+ {
+ $pkg->{deps}->{$dep} = $pkg->{deps}->{$dep_name} + 1
+ if (not defined $pkg->{deps}->{$dep});
+ }
+ }
+ }
+ delete $parser->{dep_name};
+ }
+ }
+
+ # Function calc-depends.
+ #
+ # Format:
+ # g4wihelp::config_fetch_bool "inst_FOO"
+
+ elsif ($parser->{state} eq 'function-calc-defaults')
+ {
+ if ($command eq 'FunctionEnd')
+ {
+ undef $parser->{state};
+ }
+ elsif ($command eq 'g4wihelp::config_fetch_bool')
+ {
+ fail "$file:$.: syntax error" if $#args != 0;
+
+ if ($args[0] !~ m/^"inst_(.*)"$/)
+ {
+ fail "$file:$.: syntax error";
+ }
+
+ $parser->{pkgs}->{$1}->{ini_inst} = 1;
+ }
+ }
}
+
+# MSI generator.
+# Simple indentation tracking, for pretty printing.
+$::level = 0;
+
+
sub dump_all
{
- my $pkg;
+ my ($parser) = @_;
+
+ my $pkgname;
# A running count for files within each feature.
my $fileidx;
# A running count for registry settings within each feature.
@@ -408,8 +1020,10 @@
# The current directory.
my $cdir = '';
- foreach $pkg (@::components)
+ foreach $pkgname (@{$parser->{pkg_list}})
{
+ my $pkg = $parser->{pkgs}->{$pkgname};
+
$fileidx = 0;
foreach my $file (@{$pkg->{files}})
{
@@ -455,12 +1069,12 @@
}
print ' ' x $::level
- . "<Component Id='c_$pkg->{frobbed_name}_$fileidx' Guid='"
+ . "<Component Id='c_$pkg->{name}_$fileidx' Guid='"
. get_guid ($targetfull) . "'>\n";
print ' ' x $::level
- . " <File Id='f_$pkg->{frobbed_name}_$fileidx' Name='"
- . $file->{target} . "' Source='" . $file->{source} . "'"
- . " DefaultLanguage='1033'>\n";
+ . " <File Id='f_$pkg->{name}_$fileidx' Name='"
+ . $file->{target} . "' Source='" . $file->{source} . "'>\n";
+ # Does not help to avoid the warnings: DefaultLanguage='1033'.
# EXCEPTIONS:
if ($targetfull eq 'gpgol.dll')
@@ -484,26 +1098,27 @@
}
# Create shortcuts.
- if (defined $::shortcuts{$targetfull})
+ if (defined $parser->{shortcuts}->{$targetfull})
{
+ # FIXME: Use shortcut info.
print ' ' x $::level
- . " <Shortcut Id='sm_$pkg->{frobbed_name}_$fileidx' "
+ . " <Shortcut Id='sm_$pkg->{name}_$fileidx' "
. "Directory='ProgramMenuDir' Name='$file->{target}'/>\n";
# print ' ' x $::level
-# . " <Shortcut Id='sm_$pkg->{frobbed_name}_$fileidx' "
+# . " <Shortcut Id='sm_$pkg->{name}_$fileidx' "
# . "Directory='DesktopFolder' Name='$file->{target}'/>\n";
}
print ' ' x $::level
. " </File>\n";
- if (defined $::shortcuts{$targetfull})
+ if (defined $parser->{shortcuts}->{$targetfull})
{
# http://www.mail-archive.com/wix-users@lists.sourceforge.net/msg02746.html
# -sice:ICE64
print ' ' x $::level
- . " <RemoveFolder Id='rsm_$pkg->{frobbed_name}_$fileidx' "
+ . " <RemoveFolder Id='rsm_$pkg->{name}_$fileidx' "
. "Directory='ProgramMenuDir' On='uninstall'/>\n";
}
@@ -581,10 +1196,10 @@
. '/' . $reg->{name};
print ' ' x $::level
- . "<Component Id='c_$pkg->{frobbed_name}_r_$regidx' Guid='"
+ . "<Component Id='c_$pkg->{name}_r_$regidx' Guid='"
. get_guid ($target) . "'>\n";
print ' ' x $::level
- . " <RegistryValue Id='r_$pkg->{frobbed_name}_$regidx' Root='"
+ . " <RegistryValue Id='r_$pkg->{name}_$regidx' Root='"
. $reg->{root} . "' Key='" . $reg->{key} . "' Name='"
. $reg->{name} . "' Action='write' Type='" . $reg->{type}
. "' Value='" . $reg->{value} . "'/>\n";
@@ -593,6 +1208,15 @@
$regidx++;
}
}
+
+ my @cdir = grep (!/^$/, split (/\\/, $cdir));
+ my $j;
+ for ($j = 0; $j <= $#cdir; $j++)
+ {
+ $::level -= 2;
+ print ' ' x $::level
+ . "</Directory>\n";
+ }
}
@@ -606,14 +1230,14 @@
foreach my $file (@{$pkg->{files}})
{
print ' ' x $::level
- . " <ComponentRef Id='c_$pkg->{frobbed_name}_$fileidx'/>\n";
+ . " <ComponentRef Id='c_$pkg->{name}_$fileidx'/>\n";
$fileidx++;
}
$regidx = 0;
foreach my $reg (@{$pkg->{registry}})
{
print ' ' x $::level
- . " <ComponentRef Id='c_$pkg->{frobbed_name}_r_$regidx'/>\n";
+ . " <ComponentRef Id='c_$pkg->{name}_r_$regidx'/>\n";
$regidx++;
}
}
@@ -621,10 +1245,13 @@
sub dump_all2
{
- my $pkg;
+ my ($parser) = @_;
- foreach $pkg (@::components)
+ my $pkgname;
+
+ foreach $pkgname (@{$parser->{pkg_list}})
{
+ my $pkg = $parser->{pkgs}->{$pkgname};
my $features;
next if $pkg->{hidden};
@@ -634,24 +1261,32 @@
$features .= " Description='$pkg->{description}'"
if $pkg->{description};
+ my $title = nsis_translate ($parser, '', $pkg->{title});
+
print ' ' x $::level
- . "<Feature Id='p_$pkg->{frobbed_name}' Level='$pkg->{level}' "
- . "Title='$pkg->{title}'" . $features . ">\n";
+ . "<Feature Id='p_$pkg->{name}' Level='$pkg->{level}' "
+ . "Title='$title'" . $features . ">\n";
+ if ($pkg->{ini_inst})
+ {
+ my $uc_pkgname = uc ($pkgname);
+
+ print ' ' x $::level
+ . "<Condition Level='$::nsis_level_default'>"
+ . "INST_$uc_pkgname = \"true\"</Condition>\n";
+ print ' ' x $::level
+ . "<Condition Level='$::nsis_level_optional'>"
+ . "INST_$uc_pkgname = \"false\"</Condition>\n";
+ }
+
dump_meat ($pkg);
- foreach my $dep (keys %{$::deps{$pkg->{frobbed_name}}})
+ foreach my $dep (keys %{$pkg->{deps}})
{
- my $deppkg;
-
- foreach my $_pkg (@::components)
- {
- $deppkg = $_pkg;
- last if ($_pkg->{frobbed_name} eq $dep);
- }
+ my $deppkg = $parser->{pkgs}->{$dep};
print ' ' x $::level
- . " <Feature Id='p_$pkg->{frobbed_name}_$dep' "
- . "Title='p_$pkg->{frobbed_name}_$dep' "
+ . " <Feature Id='p_$pkg->{name}_$dep' "
+ . "Title='p_$pkg->{name}_$dep' "
. "Level='$pkg->{level}' Display='hidden' "
. "InstallDefault='followParent'>\n";
$::level += 2;
@@ -666,32 +1301,103 @@
}
-# WiX is the Windows Installer XML toolset. It contains a compiler
-# (candle.exe) and a linker (light.exe) which can assemble an XML file
-# and data files to a Windows installer package (MSI).
-#
-# The following code creates an appropriate XML file for Gpg4win.
+# Just so that it is defined.
+$. = 0;
-# We use a single media element, which is also the default for all
-# components and directory elements.
+my %parser = ( pre_depth => 0, pre_true => 0 );
+my $parser = \%parser;
-# FIXME: Use Vital for all file attributes?
fetch_guids ();
-collect_all ();
-get_deps ();
-get_shortcuts ();
-$::product_id = get_guid ("/PRODUCT/$::config{_BUILD_FILEVERSION}");
-$::upgrade_code = get_guid ("/UPGRADE/1");
+while ($#ARGV >= 0 and $ARGV[0] =~ m/^-/)
+{
+ my $opt = shift @ARGV;
+ if ($opt =~ m/^--guids$/)
+ {
+ $::guid_file = shift @ARGV;
+ }
+ elsif ($opt =~ m/^--manifest$/)
+ {
+ $::files_file = shift @ARGV;
+ }
+ elsif ($opt =~ m/^-D([^=]*)=(.*)$/)
+ {
+ $parser->{pre_symbols}->{$1} = $2;
+ }
+ elsif ($opt =~ m/^-L(.*)$/)
+ {
+ $::lang = $1;
+ # Test if it is supported.
+ lang_to_lcid ($::lang);
+ }
+ elsif ($opt eq '--usage')
+ {
+ print STDERR "Usage: $0 [-DNAME=VALUE...] NSIFILE\n";
+ print STDERR "Use --help or -h for more information.\n";
+ exit 1;
+ }
+ elsif ($opt eq '-h' or $opt eq '--help')
+ {
+ print STDERR "Usage: $0 [-DNAME=VALUE...] NSIFILE\n";
+ print STDERR "Convert the .nsi file NSIFILE to a WiX source file.\n";
+ print STDERR "Options:\n";
+ print STDERR " --guids NAME Save GUIDs into file NAME (default: $::guid_file)\n";
+ print STDERR " --manifest NAME Save included files into file NAME (default: $::files_file)\n";
+ print STDERR " -DNAME=VALUE Define preprocessor symbol NAME to VALUE\n";
+ print STDERR " -LLANG Build installer for language LANG (default: $::lang)\n";
+ print STDERR "\n";
+ print STDERR " -h|--help Print this help and exit\n";
+ exit 0;
+ }
+ else
+ {
+ print STDERR "$0: unknown option $opt\n";
+ print STDERR "Usage: $0 [-DNAME=VALUE...] NSIFILE\n";
+ print STDERR "Use --help or -h for more information.\n";
+ exit 1;
+ }
+}
+
+if ($#ARGV < 0)
+{
+ nsis_parse_file ($parser, '-');
+}
+else
+{
+ nsis_parse_file ($parser, $ARGV[0]);
+}
+
+# Add exceptions.
+# ===============
+
+$parser->{pkgs}->{gnupg}->{deps}->{gpg4win} = 1;
+
+# For debugging:
+# use Data::Dumper;
+# print Dumper ($parser);
+# exit;
+
+# Dump the gathered information.
+# ==============================
+
+my $BUILD_FILEVERSION = nsis_fetch ($parser, '_BUILD_FILEVERSION');
+
+my $product_id = get_guid ("/PRODUCT/$BUILD_FILEVERSION");
+my $upgrade_code = get_guid ("/UPGRADE/1");
+
+my $INSTALL_DIR = nsis_fetch ($parser, 'INSTALL_DIR');
+
+my $lcid = lang_to_lcid ($::lang);
+
print <<EOF;
<?xml version='1.0'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product Name='Gpg4win'
- Id='$::product_id'
- UpgradeCode='$::upgrade_code'
- Language='1033'
- Version='$::config{_BUILD_FILEVERSION}'
+ Id='$product_id'
+ UpgradeCode='$upgrade_code'
+ Language='$lcid'
+ Version='$BUILD_FILEVERSION'
Manufacturer='g10 Code GmbH'>
<Package Description='Gpg4win Installer'
Comments='http://www.gpg4win.org/'
@@ -700,10 +1406,10 @@
InstallPrivileges='elevated'
Manufacturer='g10 Code GmbH'/>
- <Upgrade Id='$::upgrade_code'>
+ <Upgrade Id='$upgrade_code'>
<UpgradeVersion Property='UPGRADEPROP'
IncludeMaximum='no'
- Maximum='$::config{_BUILD_FILEVERSION}'/>
+ Maximum='$BUILD_FILEVERSION'/>
</Upgrade>
<InstallExecuteSequence>
@@ -718,18 +1424,39 @@
<Media Id='1' Cabinet='gpg4win.cab' EmbedCab='yes'/>
<Property Id="INSTDIR">
- <RegistrySearch Id='gpg4win_registry' Type='raw'
- Root='HKLM' Key='Software\\GNU\\GnuPG' Name='Install Directory' />
+ <RegistrySearch Id='gpg4win_instdir_registry' Type='raw'
+ Root='HKLM' Key='Software\\GNU\\GnuPG' Name='Install Directory'/>
+ <IniFileSearch Id='gpg4win_instdir_ini' Type='raw'
+ Name='gpg4win.ini' Section='gpg4win' Key='instdir'/>
</Property>
+EOF
+
+foreach my $pkgname (@{$parser->{pkg_list}})
+{
+ if (exists $parser->{pkgs}->{$pkgname}->{ini_inst})
+ {
+ my $uc_pkgname = uc ($pkgname);
+
+ print <<EOF;
+ <Property Id="INST_$uc_pkgname">
+ <IniFileSearch Id='gpg4win_ini_inst_$pkgname' Type='raw'
+ Name='gpg4win.ini' Section='gpg4win' Key='inst_$pkgname'/>
+ </Property>
+
+EOF
+ }
+}
+
+print <<EOF;
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='GNU' Name='GNU'>
- <Directory Id='INSTDIR' Name='$::INSTDIR'>
+ <Directory Id='INSTDIR' Name='$INSTALL_DIR'>
EOF
$::level = 12;
-dump_all ();
+dump_all ($parser);
print <<EOF;
@@ -738,11 +1465,13 @@
</Directory>
EOF
-if (scalar keys %::shortcuts)
+if (scalar keys %{$parser->{shortcuts}})
{
+ my $name = nsis_fetch ($parser, 'PRETTY_PACKAGE');
+
print <<EOF;
<Directory Id='ProgramMenuFolder' Name='PMenu'>
- <Directory Id='ProgramMenuDir' Name='$::name'/>
+ <Directory Id='ProgramMenuDir' Name='$name'/>
</Directory>
EOF
}
@@ -760,16 +1489,18 @@
EOF
$::level = 6;
-dump_all2 ();
+dump_all2 ($parser);
# <Icon Id="Foobar10.exe" SourceFile="FoobarAppl10.exe"/>
+# Removed this, because it is not localized:
+# <UIRef Id='WixUI_ErrorProgressText' />
+
print <<EOF;
</Feature>
<WixVariable Id='WixUILicenseRtf' Value='gpl.rtf'/>
<UIRef Id='WixUI_Mondo' />
- <UIRef Id='WixUI_ErrorProgressText' />
</Product>
</Wix>
@@ -780,5 +1511,4 @@
# different machine for invocation of WiX.
store_guids ();
-store_files ();
-
+store_files ($parser);
More information about the Gpg4win-commits
mailing list