Differences between revisions 2 and 13 (spanning 11 versions)
Revision 2 as of 2004-08-10 17:25:14
Size: 3796
Editor: argos
Comment: shorter variant
Revision 13 as of 2009-10-27 18:30:05
Size: 6514
Editor: EdPorteous
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
The "extending" example that comes with Py2Exe shows a nicely integrated approach for using Inno Setup to create single file executables. This example isn't so nicely integrated, but it uses [http://nsis.sourceforge.net NSIS] instead of Inno Setup in case you prefer that. The "extending" example that comes with Py2Exe shows a nicely integrated approach for using Inno Setup to create single file executables. This example isn't so nicely integrated, but it uses [[http://nsis.sourceforge.net|NSIS]] instead of Inno Setup in case you prefer that.
Line 3: Line 3:
Drop a copy of this script in your source directory alongside setup.py and modify the first two lines. The first points to py2exe's output directory and the second is the name of the executable in that directory as well as the name of the executable that NSIS will create. You can also select compression behavior - NSIS' LZMA compression (based on [http://www.7-zip.com 7-Zip]) is pretty impressive - wxPython applications start at about 3.5 - 4 MB instead of 10 - 12 MB. Compression may slow startup time for your executable somewhat. Drop a copy of this script in your source directory alongside setup.py and modify the first two lines. The first points to py2exe's output directory and the second is the name of the executable in that directory as well as the name of the executable that NSIS will create. You can also select compression behavior - NSIS' LZMA compression (based on [[http://www.7-zip.com|7-Zip]]) is pretty impressive - wxPython applications start at about 3.5 - 4 MB instead of 10 - 12 MB. Compression may slow startup time for your executable somewhat.
Line 11: Line 11:
[mailto:jimmy@retzlaff.com&subject=Single%20File%20Python%20Executables Jimmy Retzlaff] [[mailto:jimmy@retzlaff.com&subject=Single%20File%20Python%20Executables|Jimmy Retzlaff]]
Line 52: Line 52:
It also works without a temp batchfile. The current directory is also in the temp dir.
Line 53: Line 54:
The shorter version below works fine too. Tough it does not start he application in the directory it was extracted to ({{{os.curdir}}} probably points to the location of the installer; to be verified). But the application can find out its own location by using {{{os.path.dirname(sys.argv[0])}}}. There is a streamlined version below. The sample python script 'simple.py' pops up an dialog box with the paths to important directories.
uncompressed size is around 1.5MB, "lzma" compression leads to approx. 500kB. Just save the three files below in a directory then run {{{setup.py}}} and then {{{makensis setup.nsi}}}. Compare the outputs of the exe to directly running {{{single.py}}}. (ctypes has to be installed to create the exe)
Line 57: Line 59:
Commenting out {{{compressor}}} disables compression.
Line 59: Line 63:
'''setup.nsi:''' Enhanced to use the correct working directory, pass through command line parameters and the exit code.

'''setup.nsi''' - the Nullsoft installer script
Line 61: Line 67:
!define py2exeOutputDirectory 'dist\Calculator'
!define exe 'calculator.exe'
 
; Comment out the "SetCompress Off" line and uncomment
; the next line to enable compression. Startup times
; will be a little slower but the executable will be
; quite a bit smaller
SetCompress Off
;SetCompressor lzma
!define py2exeOutputDir 'dist'
!define exe 'single.exe'
!define icon 'C:\python23\py.ico'
!define compressor 'lzma' ;one of 'zlib', 'bzip2', 'lzma'
!define onlyOneInstance
Line 71: Line 73:
; - - - - do not edit below this line, normaly - - - -
!ifdef compressor
    SetCompressor ${compressor}
!else
    SetCompress Off
!endif
Line 74: Line 82:
;Icon 'icon.ico' !ifdef icon
    Icon ${icon}
!endif

; - - - - Allow only one installer instance - - - -
!ifdef onlyOneInstance
Function .onInit
 System::Call "kernel32::CreateMutexA(i 0, i 0, t '$(^Name)') i .r0 ?e"
 Pop $0
 StrCmp $0 0 launch
  Abort
 launch:
FunctionEnd
!endif
; - - - - Allow only one installer instance - - - -
Line 77: Line 99:
         ; Get directory from which the exe was called
    System::Call "kernel32::GetCurrentDirectory(i ${NSIS_MAX_STRLEN}, t .r0)"
    
    ; Unzip into pluginsdir
Line 79: Line 106:
    File /r '${py2exeOutputDirectory}\*.*'
    nsExec::Exec $PLUGINSDIR\${exe}
    File /r '${py2exeOutputDir}\*.*'
    
    ; Set working dir and execute, passing through commandline params
    SetOutPath '$0'
    ${GetParameters} $R0
    ExecWait '"$PLUGINSDIR\${exe}" $R0' $R2
    SetErrorLevel $R2
 
Line 83: Line 116:

'''single.py''' - the example script
{{{
#! python
import os, sys, ctypes

ctypes.windll.user32.MessageBoxA(0,
    "curdir: %s\nexedir: %s\nsys.winver: %s" % (
        os.path.abspath(os.curdir),
        os.path.abspath(os.path.dirname(sys.argv[0])),
        sys.winver,
    ), "%s - Message" % os.path.basename(sys.executable), 0x30
)
}}}

'''setup.py''' - just run it to make the sample
{{{
#! python
from distutils.core import setup
import py2exe, sys, os

sys.argv.append('py2exe')

setup(
    options = {'py2exe': {'optimize': 2}},
    windows = [{'script': "single.py"}],
    zipfile = "shared.lib",
)
}}}

A NSI along these lines works unter Win NT 4 too.

----

== Using "bundle_files" and "zipfile" ==

An easier (and better) way to create single-file executables is to set {{{bundle_files}}} to {{{1}}} or {{{2}}}, and to set {{{zipfile}}} to {{{None}}}. This approach does not require extracting files to a temporary location, which provides much faster program startup.

Valid values for {{{bundle_files}}} are:

|| 3 (default) || don't bundle ||
|| 2 || bundle everything but the Python interpreter ||
|| 1 || bundle everything, including the Python interpreter ||

If {{{zipfile}}} is set to {{{None}}}, the files will be bundle within the executable instead of {{{library.zip}}}.

Here is a sample '''setup.py''':

{{{
#! python
from distutils.core import setup
import py2exe, sys, os

sys.argv.append('py2exe')

setup(
    options = {'py2exe': {'bundle_files': 1}},
    windows = [{'script': "single.py"}],
    zipfile = None,
)
}}}

The "extending" example that comes with Py2Exe shows a nicely integrated approach for using Inno Setup to create single file executables. This example isn't so nicely integrated, but it uses NSIS instead of Inno Setup in case you prefer that.

Drop a copy of this script in your source directory alongside setup.py and modify the first two lines. The first points to py2exe's output directory and the second is the name of the executable in that directory as well as the name of the executable that NSIS will create. You can also select compression behavior - NSIS' LZMA compression (based on 7-Zip) is pretty impressive - wxPython applications start at about 3.5 - 4 MB instead of 10 - 12 MB. Compression may slow startup time for your executable somewhat.

Once you've built your executable with py2exe, then compile the installer script with NSIS and an executable will be created in the same folder as the script. When run, that single file executable will expand the original executable created by py2exe along with all the dll, pyd, and data files for your application into a temporary directory and run it. When your application exits, the temp folder will be deleted automatically.

Command line parameters for your executable are not supported.

The executables produced have only been tested on Windows XP. Please update this page if you try them on other platforms.

Jimmy Retzlaff


setup.nsi:

!define py2exeOutputDirectory 'dist\Calculator'
!define exe 'calculator.exe'

; Comment out the "SetCompress Off" line and uncomment
; the next line to enable compression. Startup times
; will be a little slower but the executable will be
; quite a bit smaller
SetCompress Off
;SetCompressor lzma

Name 'Calculator'
OutFile ${exe}
SilentInstall silent
;Icon 'icon.ico'

Section
    InitPluginsDir
    SetOutPath '$PLUGINSDIR'
    File '${py2exeOutputDirectory}\*.*'

    GetTempFileName $0
    DetailPrint $0
    Delete $0
    StrCpy $0 '$0.bat'
    FileOpen $1 $0 'w'
    FileWrite $1 '@echo off$\r$\n'
    StrCpy $2 $TEMP 2
    FileWrite $1 '$2$\r$\n'
    FileWrite $1 'cd $PLUGINSDIR$\r$\n'
    FileWrite $1 '${exe}$\r$\n'
    FileClose $1
    nsExec::Exec $0
    Delete $0
SectionEnd

The version above seems to work fine on Win9x. Even with the batch file that is lauched (and DOS boxes do not close automaticaly, by default, on those OS). The startup time of a wxPython app with lzma compression becomes very long (1+ minute) on a machinne with 64MB RAM and a Pentium 200MHz, but it's usable on faster machines that are common today ;-) It also works without a temp batchfile. The current directory is also in the temp dir.

There is a streamlined version below. The sample python script 'simple.py' pops up an dialog box with the paths to important directories. uncompressed size is around 1.5MB, "lzma" compression leads to approx. 500kB. Just save the three files below in a directory then run setup.py and then makensis setup.nsi. Compare the outputs of the exe to directly running single.py. (ctypes has to be installed to create the exe)

I also added a "/r" so that subdirectories are also packed into the installer as i tend to keep images and other data as external files in a subdir, even with py2exe.

Commenting out compressor disables compression.

cliechti

Enhanced to use the correct working directory, pass through command line parameters and the exit code.

setup.nsi - the Nullsoft installer script

!define py2exeOutputDir 'dist'
!define exe             'single.exe'
!define icon            'C:\python23\py.ico'
!define compressor      'lzma'  ;one of 'zlib', 'bzip2', 'lzma'
!define onlyOneInstance

; - - - - do not edit below this line, normaly - - - -
!ifdef compressor
    SetCompressor ${compressor}
!else
    SetCompress Off
!endif
Name ${exe}
OutFile ${exe}
SilentInstall silent
!ifdef icon
    Icon ${icon}
!endif

; - - - - Allow only one installer instance - - - - 
!ifdef onlyOneInstance
Function .onInit
 System::Call "kernel32::CreateMutexA(i 0, i 0, t '$(^Name)') i .r0 ?e"
 Pop $0
 StrCmp $0 0 launch
  Abort
 launch:
FunctionEnd
!endif
; - - - - Allow only one installer instance - - - - 

Section
    
    ; Get directory from which the exe was called
    System::Call "kernel32::GetCurrentDirectory(i ${NSIS_MAX_STRLEN}, t .r0)"
    
    ; Unzip into pluginsdir
    InitPluginsDir
    SetOutPath '$PLUGINSDIR'
    File /r '${py2exeOutputDir}\*.*'
    
    ; Set working dir and execute, passing through commandline params
    SetOutPath '$0'
    ${GetParameters} $R0
    ExecWait '"$PLUGINSDIR\${exe}" $R0' $R2
    SetErrorLevel $R2
 
SectionEnd

single.py - the example script

   1 import os, sys, ctypes
   2 
   3 ctypes.windll.user32.MessageBoxA(0,
   4     "curdir: %s\nexedir: %s\nsys.winver: %s" % (
   5         os.path.abspath(os.curdir),
   6         os.path.abspath(os.path.dirname(sys.argv[0])),
   7         sys.winver,
   8     ), "%s - Message" % os.path.basename(sys.executable), 0x30
   9 )

setup.py - just run it to make the sample

   1 from distutils.core import setup
   2 import py2exe, sys, os
   3 
   4 sys.argv.append('py2exe')
   5 
   6 setup(
   7     options = {'py2exe': {'optimize': 2}},
   8     windows = [{'script': "single.py"}],
   9     zipfile = "shared.lib",
  10 )

A NSI along these lines works unter Win NT 4 too.


Using "bundle_files" and "zipfile"

An easier (and better) way to create single-file executables is to set bundle_files to 1 or 2, and to set zipfile to None. This approach does not require extracting files to a temporary location, which provides much faster program startup.

Valid values for bundle_files are:

3 (default)

don't bundle

2

bundle everything but the Python interpreter

1

bundle everything, including the Python interpreter

If zipfile is set to None, the files will be bundle within the executable instead of library.zip.

Here is a sample setup.py:

   1 from distutils.core import setup
   2 import py2exe, sys, os
   3 
   4 sys.argv.append('py2exe')
   5 
   6 setup(
   7     options = {'py2exe': {'bundle_files': 1}},
   8     windows = [{'script': "single.py"}],
   9     zipfile = None,
  10 )

SingleFileExecutable (last edited 2015-04-03 16:23:23 by JimmyRetzlaff)