3 # NSISWrapper - a helper program for making Windows installers.
4 # Copyright (C) 2008 Red Hat Inc.
5 # Written by Richard W.M. Jones <rjones@redhat.com>,
6 # http://et.redhat.com/~rjones
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or (at
11 # your option) any later version.
13 # This program is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 nsiswrapper - Helper program for making NSIS Windows installers
34 nsiswrapper [options] [roots...]
36 nsiswrapper myprogram.exe anotherprog.exe docs/ > script.nsis
38 nsiswrapper --run myprogram.exe anotherprog.exe docs/
42 nsiswrapper is a helper program for making it easier to create Windows
43 installers in a cross-compiler environment. It still requires NSIS (a
44 Windows installer generator) but cuts out the tedium of writing the
45 NSIS command script, and can even invoke NSIS automatically to
46 generate a final Windows executable.
48 The general way to use it is to list out some files that you want
49 packaged. For example:
51 nsiswrapper myprogram.exe
53 This will search for C<myprogram.exe> and any libraries (C<*.dll>)
54 that it depends upon, and then it will print out an NSIS script.
56 If you want to have it run C<makensis> as well (to automatically
57 create a Windows installer) then do:
59 nsiswrapper --run myprogram.exe
61 which will generate C<installer.exe> output file that contains
62 C<myprogram.exe> plus any dependencies.
64 You can list other files and directories that you want to have
65 contained in your installer. For example:
67 nsiswrapper myprogram.exe anotherprog.exe docs/*.html
69 There are many other command line options which control aspects of the
70 NSIS command script (and hence, the final installer), such as:
76 The name of the final installer.
80 Desktop shortcuts and menu items.
88 It's a good idea to examine the NSIS command script, to check that
89 nsiswrapper is including all the right dependencies.
97 Print brief help message and exit.
101 Print the full manual page for the command and exit.
105 Print verbose messages while running. If this is not given then we
106 try to operate quietly.
110 Normally this program just prints out the NSIS installer command
111 script. However if you supply this option, then we run C<makensis>
112 and attempt to generate an actual Windows installer.
114 =item B<--name "Name">
116 Set the long name of the installer.
118 If not set, the script tries to invent a suitable name based on the
119 first root file given on the command line.
121 See also B<--outfile>.
123 =item B<--outfile myinstaller.exe>
125 Set the output filename for the installer.
127 If not set, this defaults to C<installer.exe>.
129 This is the same as the C<OutFile> option to NSIS.
131 =item B<--installdir 'C:\foo'>
133 Set the default Windows installation directory. If not set, this
134 program will choose a suitable default based on the name.
136 In any case, the end user can override this when they run the
139 Note that since this string will contain backslashes, you should
140 single-quote it to protect it from the shell.
142 This is the same as the C<InstallDir> option to NSIS.
144 =item B<--installdirregkey 'HKLM SOFTWARE\FOO'>
146 Set the name of the registry key used to save the installation
147 directory. This has two purposes: Firstly it is used to automagically
148 remember the installation directory between installs. Secondly your
149 program can use this as one method to find its own installation
150 directory (there are other ways to do this).
152 The default is C<HKLM SOFTWARE\Name> where C<Name> is derived from the
153 name of the installer.
155 Note that since this string will contain backslashes and spaces, you
156 should single-quote it to protect it from the shell.
158 This is the same as the C<InstallDirRegKey> option to NSIS.
170 my $outfile = 'installer.exe';
172 my $installdirregkey = '';
176 my $result = GetOptions (
179 "verbose" => \$verbose,
181 "outfile=s" => \$outfile,
182 "installdir=s" => \$installdir,
183 "installdirregkey=s" => \$installdirregkey,
185 die "nsiswrapper: use --help for information about command line options\n"
188 pod2usage(1) if $help;
189 pod2usage(-exitstatus => 0, -verbose => 2) if $man;
191 # Add the roots to the list of files.
192 die "nsiswrapper: no roots specified: use --help for more help\n"
196 $exec = 1 if m/\.exe$/i;
208 # Massage the first root into a suitable package name.
216 # InstallDir not set?
220 $installdir = "c:\\$_"
223 # InstallDirRegKey not set?
224 if (!$installdirregkey) {
227 $installdirregkey = "HKLM SOFTWARE\\$_"
231 # Check prerequisites.
235 my @paths = split (/:/, $ENV{PATH});
238 $objdump = check_path ("i686-pc-mingw32-objdump", @paths);
239 if (! $objdump || ! -x $objdump) {
240 die "i686-pc-mingw32-objdump: program not found on \$PATH\n"
245 # Check for the existance of a file at the given paths (not
246 # necessarily executable). Returns the pathname of the file or
247 # undefined if not found.
255 foreach my $dir (@paths) {
256 my $file = $dir . "/" . $_;
265 # Print configuration.
269 print "Configuration:\n";
270 print "\t\$PATH = $ENV{PATH}\n";
271 print "\t\$objdump = $objdump\n";
272 print "\t\$verbose = $verbose\n";
273 print "\t\$name = \"$name\"\n";
274 print "\t\$outfile = \"$outfile\"\n";
275 print "\t\$installdir = \"$installdir\"\n";
276 print "\t\$installdirregkey = \"$installdirregkey\"\n";
277 my @roots = keys %files;
278 print "\t\@roots = (", join (", ", @roots), ")\n";
279 print "End of configuration.\n";
282 # Starting at the roots, get the dependencies.
290 foreach (keys %files) {
291 my @deps = get_deps_for_file ($_);
293 # Add the deps as separate files.
295 unless (exists $files{$_}) {
309 my $path_warning = 0;
311 sub get_deps_for_file
314 my @paths = split (/:/, $ENV{PATH});
316 # If we already fetched the dependencies for this file, just
317 # return that list now.
318 if (exists $files{$file}->{deps}) {
319 return @{$files{$file}->{deps}}
324 # We only know how to do this for *.exe and *.dll files.
325 if (m/\.exe$/i || m/\.dll$/i) {
326 my $cmd = "$objdump -p '$file' |
328 grep -Eo '[-._[:alnum:]]+\.dll' |
329 sort -u"; # XXX quoting
330 open DEPS, "$cmd |" or die "$cmd: $!";
334 # Ignore Windows system DLL deps.
335 next if is_windows_system_dll ($_);
337 # Does the file exist on the path?
338 my $found = check_path ($_, @paths);
342 warn "MISSING DEPENDENCY: $_ (for $file)\n";
343 unless ($path_warning) {
344 warn "You may need to add the directory containing this file to your \$PATH\n";
353 print "dependencies found for binary $file:\n\t",
354 join ("\n\t", @deps), "\n";
356 print "no dependencies found for $file\n"
362 # Cache the list of dependencies so we can just return it
363 # immediately next time.
364 $files{$file}->{deps} = \@deps;
368 sub is_windows_system_dll
373 $_ eq 'kernel32.dll' ||
375 $_ eq 'mscoree.dll' ||
376 $_ eq 'msvcrt.dll' ||
380 # Decide how we will name the output files. This removes the
381 # common prefix from filenames, if it can determine one.
385 my @names = keys %files;
387 # Determine if all the names share a common prefix.
388 my @namelens = map { length } @names;
389 my $shortest = min (@namelens);
392 for ($prefixlen = $shortest; $prefixlen >= 0; --$prefixlen) {
393 my @ns = map { $_ = substr $_, 0, $prefixlen } @names;
397 if ($verbose) { print "prefix length = $prefixlen\n" }
399 # Remove the prefix from each name and save the install directory
400 # and install filename separately.
401 foreach my $name (keys %files) {
402 my $install_as = substr $name, $prefixlen;
404 my ($install_dir, $install_name);
406 if ($install_as =~ m{(.*)/(.*)}) {
411 $install_name = $install_as;
414 # Convert / in install_dir into backslashes.
415 $install_dir =~ s{/}{\\}g;
417 $files{$name}->{install_dir} = $install_dir;
418 $files{$name}->{install_name} = $install_name;
426 $max = $_ if $_ > $max;
435 $min = $_ if $_ < $min;
444 return 0 if $_ ne $s;
449 # Print the list of files.
454 foreach (sort keys %files) {
456 if ($files{$_}->{root}) {
459 if ($files{$_}->{dir}) {
462 print STDOUT ("\n\t => ",
463 $files{$_}->{install_dir}, " \\ ", $files{$_}->{install_name},
466 print "End of files.\n";
469 # Write the NSIS script.
476 #!Nsis Installer Command Script
478 # This is an NSIS Installer Command Script generated automatically
479 # by the Red Hat nsiswrapper program. For more information see:
481 # http://et.redhat.com/~rjones/
483 # To build an installer from the script you would normally do:
485 # makensis this_script
487 # which will generate the output file '$outfile' which is a Windows
488 # installer containing your program.
492 InstallDir "$installdir"
493 InstallDirRegKey $installdirregkey "Install_Dir"
496 ShowUninstDetails hide
498 # Uncomment this to enable BZip2 compression, which results in
499 # slightly smaller files but uses more memory at install time.
508 ComponentText "Select which optional components you want to install."
510 DirText "Please select the installation folder."
516 # Set the output files.
518 foreach (sort keys %files) {
519 if (!$olddir || $files{$_}->{install_dir} ne $olddir) {
520 # Moved into a new install directory.
521 my $dir = $files{$_}->{install_dir};
522 print $io "\n SetOutPath \"\$INSTDIR\\$dir\"\n";
526 # If it's a directory, we copy it recursively, otherwise
527 # just copy the single file.
528 if ($files{$_}->{dir}) {
529 print $io " File /r \"$_\"\n";
531 print $io " File \"$_\"\n";
538 Section "Start Menu Shortcuts"
539 CreateDirectory "\$SMPROGRAMS\\$name"
540 CreateShortCut "\$SMPROGRAMS\\$name\\Uninstall $name.lnk" "\$INSTDIR\\Uninstall $name.exe" "" "\$INSTDIR\\Uninstall $name.exe" 0
543 # Start menu entries for each executable.
544 foreach (sort keys %files) {
545 if ($files{$_}->{exec}) {
546 my $install_dir = $files{$_}->{install_dir};
547 my $install_name = $files{$_}->{install_name};
548 print $io " CreateShortCut \"\$SMPROGRAMS\\$name\\$install_name.lnk\" \"\$INSTDIR\\$install_dir\\$install_name\" \"\" \"\$INSTDIR\\$install_dir\\$install_name\" 0\n";
555 Section "Desktop Icons"
558 # Desktop icons for each executable.
559 foreach (sort keys %files) {
560 if ($files{$_}->{exec}) {
561 my $install_dir = $files{$_}->{install_dir};
562 my $install_name = $files{$_}->{install_name};
563 print $io " CreateShortCut \"\$DESKTOP\\$install_name.lnk\" \"\$INSTDIR\\$install_dir\\$install_name\" \"\" \"\$INSTDIR\\$install_dir\\$install_name\" 0\n";
573 # Remove desktop icons and menu shortcuts.
574 foreach (reverse sort keys %files) {
575 if ($files{$_}->{exec}) {
576 my $install_name = $files{$_}->{install_name};
577 print $io " Delete /rebootok \"\$DESKTOP\\$install_name.lnk\"\n";
578 print $io " Delete /rebootok \"\$SMPROGRAMS\\$name\\$install_name.lnk\"\n";
581 print $io " Delete /rebootok \"\$SMPROGRAMS\\$name\\Uninstall $name.lnk\"\n\n";
583 # Remove remaining files.
585 foreach (reverse sort keys %files) {
586 if (!$olddir || $files{$_}->{install_dir} ne $olddir) {
587 # Moved into a new install directory, so delete the previous one.
588 print $io " RMDir \"\$INSTDIR\\$olddir\"\n\n"
590 $olddir = $files{$_}->{install_dir};
593 # If it's a directory, we delete it recursively, otherwise
594 # just delete the single file.
595 my $install_dir = $files{$_}->{install_dir};
596 my $install_name = $files{$_}->{install_name};
597 if ($files{$_}->{dir}) {
598 print $io " RMDir /r \"\$INSTDIR\\$install_dir\"\n\n";
599 $olddir = ''; # Don't double-delete directory.
601 print $io " Delete /rebootok \"\$INSTDIR\\$install_dir\\$install_name\"\n";
605 print $io " RMDir \"\$INSTDIR\\$olddir\"\n" if $olddir;
612 WriteUninstaller "\$INSTDIR\\Uninstall $name.exe"
624 print_config () if $verbose;
627 print_files () if $verbose;
628 write_script (\*STDOUT);