Downloads: https://github.com/apnadkarni/tcl-sfe/releases
Repository: https://github.com/apnadkarni/tcl-sfe
Docs: https://github.com/apnadkarni/tcl-sfe/blob/main/README.md
This repository hosts single file executables (SFE) of Tcl/Tk for
Windows and a build system to create and customize them. These are
completely statically linked (other than the C and Windows runtimes) and therefore avoid issues that arise from similar single file executables
that write shared libraries to disk.
Downloads: https://github.com/apnadkarni/tcl-sfe/releases
Repository: https://github.com/apnadkarni/tcl-sfe
Docs: https://github.com/apnadkarni/tcl-sfe/blob/main/README.md
This repository hosts single file executables (SFE) of Tcl/Tk for
Windows and a build system to create and customize them. These are
completely statically linked (other than the C and Windows runtimes) and therefore avoid issues that arise from similar single file executables
that write shared libraries to disk.
The `tclsfe.exe` and `tksfe.exe` programs are enhanced versions of `tclsh.exe` and `wish.exe` that include the following extensions by
default:
- thread
- sqlite3
- tdbc::odbc
- twapi
The build system allows omission of any of these if smaller executables
are desired. Further, SFE's can be customized by adding
- additional packages or Tcl modules
- interpreter initalization scripts
- a main script that implements a complete application
- statically linked extensions
Only the last customization needs a compiler.
Wow, this is fantastic Ashok!
I downloaded tksfe.exe and since I already had a magicsplat 9.0.2 in a side directory, I tried the following command:
-a-a sfe::make newsingle.exe {*}[lmap d [glob -directory {D:/magic/Tcl902/lib} -types d *] {if {[file tail $d] in {tcl9}} continue ; set d}]
This built a 34-megabyte single file executable. Note the suppression of the tcl9 directory, which had previously given an error:
-a-a-a VFS path A:/temp/tcl_9XK4MHMB/tcl9 exists and either A:/temp/tcl_9XK4MHMB/tcl9
-a-a-a or D:/magic/Tcl902/lib/tcl9 is a directory.
I then went through every package with a .dll I could find and ran package require on each of them, verifying via Process Explorer that the DLL really did get copied out to the temp directory rather than being found some other way.
The only failure I found was:
-a-a-a % package require Blend2d
-a-a-a couldn't load from current filesystem
This works fine against a plain disk install of Tcl902, so something about the SFE/zipfs environment trips it up.
I also tested that I could create another single file .exe from the first one I created with all the packages included. Here I simply added a main.tcl, and that worked.
The only thing I'm wondering is when a new tcl/tk release comes out, it would be handy to accept a .exe as a path argument to be able to read the prior sfe and propagate everything into a new tcl/tk release. So, for example, this should be able to update an existing sfe quite easily:
-a-a-a > tksfe.exe (a new version)
-a-a-a-a-a % package require sfe
-a-a-a-a-a % sfe::make newsingleV2.exe newsingle.exe
Your sfe is a great way to create a fully batteries-included single file executable!!!
Outstanding work!
Eric
On 7/3/2026 8:02 PM, Ashok wrote:
Downloads: https://github.com/apnadkarni/tcl-sfe/releases
Repository: https://github.com/apnadkarni/tcl-sfe
Docs: https://github.com/apnadkarni/tcl-sfe/blob/main/README.md
This repository hosts single file executables (SFE) of Tcl/Tk for
Windows and a build system to create and customize them. These are
completely statically linked (other than the C and Windows runtimes) and
therefore avoid issues that arise from similar single file executables
that write shared libraries to disk.
The `tclsfe.exe` and `tksfe.exe` programs are enhanced versions of
`tclsh.exe` and `wish.exe` that include the following extensions by
default:
- thread
- sqlite3
- tdbc::odbc
- twapi
The build system allows omission of any of these if smaller executables
are desired. Further, SFE's can be customized by adding
- additional packages or Tcl modules
- interpreter initalization scripts
- a main script that implements a complete application
- statically linked extensions
Only the last customization needs a compiler.
Wow, this is fantastic Ashok!
I downloaded tksfe.exe and since I already had a magicsplat 9.0.2 in a
side directory, I tried the following command:
-a-a sfe::make newsingle.exe {*}[lmap d [glob -directory {D:/magic/ Tcl902/lib} -types d *] {if {[file tail $d] in {tcl9}} continue ; set d}]
This built a 34-megabyte single file executable. Note the suppression of
the tcl9 directory, which had previously given an error:
-a-a-a VFS path A:/temp/tcl_9XK4MHMB/tcl9 exists and either A:/temp/ tcl_9XK4MHMB/tcl9
-a-a-a or D:/magic/Tcl902/lib/tcl9 is a directory.
I then went through every package with a .dll I could find and ran
package require on each of them, verifying via Process Explorer that the
DLL really did get copied out to the temp directory rather than being
found some other way.
The only failure I found was:
-a-a-a % package require Blend2d
-a-a-a couldn't load from current filesystem
This works fine against a plain disk install of Tcl902, so something
about the SFE/zipfs environment trips it up.
I also tested that I could create another single file .exe from the
first one I created with all the packages included. Here I simply added
a main.tcl, and that worked.
The only thing I'm wondering is when a new tcl/tk release comes out, it would be handy to accept a .exe as a path argument to be able to read
the prior sfe and propagate everything into a new tcl/tk release. So,
for example, this should be able to update an existing sfe quite easily:
-a-a-a > tksfe.exe (a new version)
-a-a-a-a-a % package require sfe
-a-a-a-a-a % sfe::make newsingleV2.exe newsingle.exe
Your sfe is a great way to create a fully batteries-included single file executable!!!
Outstanding work!
Eric
On 7/3/2026 8:02 PM, Ashok wrote:
Downloads: https://github.com/apnadkarni/tcl-sfe/releases
Repository: https://github.com/apnadkarni/tcl-sfe
Docs: https://github.com/apnadkarni/tcl-sfe/blob/main/README.md
This repository hosts single file executables (SFE) of Tcl/Tk for
Windows and a build system to create and customize them. These are
completely statically linked (other than the C and Windows runtimes) and
therefore avoid issues that arise from similar single file executables
that write shared libraries to disk.
The `tclsfe.exe` and `tksfe.exe` programs are enhanced versions of
`tclsh.exe` and `wish.exe` that include the following extensions by
default:
- thread
- sqlite3
- tdbc::odbc
- twapi
The build system allows omission of any of these if smaller executables
are desired. Further, SFE's can be customized by adding
- additional packages or Tcl modules
- interpreter initalization scripts
- a main script that implements a complete application
- statically linked extensions
Only the last customization needs a compiler.
I thought I would take a stab at adding a feature to Ashok's sfe, since
I get to play with zips and some OO code. So, I added an option, "-exe filename" and a helper function to do the work:
-a-a-a method addFromSfe {exePath} {
-a-a-a-a-a-a-a # Merges the package content of another SFE into this VFS, skipping
-a-a-a-a-a-a-a # any top-level entry that already exists (so the current base
-a-a-a-a-a-a-a # runtime's own bundled packages always take precedence over
-a-a-a-a-a-a-a # same-named ones carried in from an older SFE).
-a-a-a-a-a-a-a set mnt //zipfs:/sfemerge_[pid]
-a-a-a-a-a-a-a zipfs mount $exePath $mnt
-a-a-a-a-a-a-a try {
-a-a-a-a-a-a-a-a-a-a-a set vfsDir [my getVfsDir]
-a-a-a-a-a-a-a-a-a-a-a foreach srcPath [glob [file join $mnt *]] {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a set destPath [file join $vfsDir [file tail $srcPath]]
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a if {[file exists $destPath]} {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a puts stderr "Note: skipping [file tail $srcPath]
from $exePath - already present from base runtime."
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a continue
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a file copy -force -- $srcPath $vfsDir
-a-a-a-a-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a } finally {
-a-a-a-a-a-a-a-a-a-a-a zipfs unmount $mnt
-a-a-a-a-a-a-a }
-a-a-a }
proc sfe::make {outPath args} {
-a-a-a # Creates a new single-file executable from the current one with the
-a-a-a # addition of paths. If a path is a directory, it is copied along with
-a-a-a # its content to the top level of the vfs. If an ordinary file, it is
-a-a-a # copied to a directory in Tcl module path if it has a .tm extension
-a-a-a # or the top level of the vfs. A path preceded by -exe is treated as
-a-a-a # a prior SFE whose package content should be merged in; -exe may
-a-a-a # appear any number of times, in any position, in the argument list.
-a-a-a set sfe [SfeMaker new]
-a-a-a try {
-a-a-a-a-a-a-a set expectExe 0
-a-a-a-a-a-a-a foreach arg $args {
-a-a-a-a-a-a-a-a-a-a-a if {$expectExe} {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a $sfe addFromSfe [file normalize $arg]
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a set expectExe 0
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a continue
-a-a-a-a-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a-a-a-a-a if {$arg eq "-exe"} {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a set expectExe 1
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a continue
-a-a-a-a-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a-a-a-a-a set path [file normalize $arg]
-a-a-a-a-a-a-a-a-a-a-a set type [file type $path]
-a-a-a-a-a-a-a-a-a-a-a switch $type {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a directory {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a $sfe addPackage $path
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a file {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a if {[string equal -nocase [file extension $path]
".tm"]} {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a $sfe addModule $path
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a } else {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a $sfe addPath $path [file tail $path]
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a default {
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a puts stderr "Warning: Skipping $path of type $type."
-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a if {$expectExe} {
-a-a-a-a-a-a-a-a-a-a-a error "-exe requires a following path argument."
-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a $sfe buildSfe $outPath
-a-a-a } finally {
-a-a-a-a-a-a-a $sfe destroy
-a-a-a }
}
On 7/5/2026 1:59 PM, et99 wrote:
Wow, this is fantastic Ashok!
I downloaded tksfe.exe and since I already had a magicsplat 9.0.2 in a
side directory, I tried the following command:
-a-a-a sfe::make newsingle.exe {*}[lmap d [glob -directory {D:/magic/
Tcl902/lib} -types d *] {if {[file tail $d] in {tcl9}} continue ; set d}]
This built a 34-megabyte single file executable. Note the suppression
of the tcl9 directory, which had previously given an error:
-a-a-a-a VFS path A:/temp/tcl_9XK4MHMB/tcl9 exists and either A:/temp/
tcl_9XK4MHMB/tcl9
-a-a-a-a or D:/magic/Tcl902/lib/tcl9 is a directory.
I then went through every package with a .dll I could find and ran
package require on each of them, verifying via Process Explorer that
the DLL really did get copied out to the temp directory rather than
being found some other way.
The only failure I found was:
-a-a-a-a % package require Blend2d
-a-a-a-a couldn't load from current filesystem
This works fine against a plain disk install of Tcl902, so something
about the SFE/zipfs environment trips it up.
I also tested that I could create another single file .exe from the
first one I created with all the packages included. Here I simply
added a main.tcl, and that worked.
The only thing I'm wondering is when a new tcl/tk release comes out,
it would be handy to accept a .exe as a path argument to be able to
read the prior sfe and propagate everything into a new tcl/tk release.
So, for example, this should be able to update an existing sfe quite
easily:
-a-a-a-a > tksfe.exe (a new version)
-a-a-a-a-a-a % package require sfe
-a-a-a-a-a-a % sfe::make newsingleV2.exe newsingle.exe
Your sfe is a great way to create a fully batteries-included single
file executable!!!
Outstanding work!
Eric
On 7/3/2026 8:02 PM, Ashok wrote:
Downloads: https://github.com/apnadkarni/tcl-sfe/releases
Repository: https://github.com/apnadkarni/tcl-sfe
Docs: https://github.com/apnadkarni/tcl-sfe/blob/main/README.md
This repository hosts single file executables (SFE) of Tcl/Tk for
Windows and a build system to create and customize them. These are
completely statically linked (other than the C and Windows runtimes) and >>> therefore avoid issues that arise from similar single file executables
that write shared libraries to disk.
The `tclsfe.exe` and `tksfe.exe` programs are enhanced versions of
`tclsh.exe` and `wish.exe` that include the following extensions by
default:
- thread
- sqlite3
- tdbc::odbc
- twapi
The build system allows omission of any of these if smaller executables
are desired. Further, SFE's can be customized by adding
- additional packages or Tcl modules
- interpreter initalization scripts
- a main script that implements a complete application
- statically linked extensions
Only the last customization needs a compiler.
Eric,
Thanks for testing!
Yes, the Tcl9 suppression is required because overwriting of directories is explicitly rejected, to avoid confusion between target directories overlapping. Since sfe itself already has Tcl 9 script files, that is a conflict if you try to add external Tcl 9 core scripts.
The Blend2d failure is probably not specific to sfe but rather the same issue with embedded DLL's being dependent on other DLL's in any zipkit/tclkit system. They have to be explicitly copied out to disk.
BTW, note that adding packages that include binary extensions means they are written out to disk. In that case, you might as well be using any zipkit/tclkit. The uniqueness of tclsfe is only static linking.
Regarding copying over of files for new releases, currently there is no tracking of which files came from Tcl and which came from extensions. I think the simpler solution is just to have a script that adds packages that you can then run every time a new tclsfe based on a new Tcl release comes out.
On 7/6/2026 2:29 AM, et99 wrote:
Wow, this is fantastic Ashok!
I downloaded tksfe.exe and since I already had a magicsplat 9.0.2 in a side directory, I tried the following command:
-a-a-a sfe::make newsingle.exe {*}[lmap d [glob -directory {D:/magic/ Tcl902/lib} -types d *] {if {[file tail $d] in {tcl9}} continue ; set d}]
This built a 34-megabyte single file executable. Note the suppression of the tcl9 directory, which had previously given an error:
-a-a-a-a VFS path A:/temp/tcl_9XK4MHMB/tcl9 exists and either A:/temp/ tcl_9XK4MHMB/tcl9
-a-a-a-a or D:/magic/Tcl902/lib/tcl9 is a directory.
I then went through every package with a .dll I could find and ran package require on each of them, verifying via Process Explorer that the DLL really did get copied out to the temp directory rather than being found some other way.
The only failure I found was:
-a-a-a-a % package require Blend2d
-a-a-a-a couldn't load from current filesystem
This works fine against a plain disk install of Tcl902, so something about the SFE/zipfs environment trips it up.
I also tested that I could create another single file .exe from the first one I created with all the packages included. Here I simply added a main.tcl, and that worked.
The only thing I'm wondering is when a new tcl/tk release comes out, it would be handy to accept a .exe as a path argument to be able to read the prior sfe and propagate everything into a new tcl/tk release. So, for example, this should be able to update an existing sfe quite easily:
-a-a-a-a > tksfe.exe (a new version)
-a-a-a-a-a-a % package require sfe
-a-a-a-a-a-a % sfe::make newsingleV2.exe newsingle.exe
Your sfe is a great way to create a fully batteries-included single file executable!!!
Outstanding work!
Eric
On 7/3/2026 8:02 PM, Ashok wrote:
Downloads: https://github.com/apnadkarni/tcl-sfe/releases
Repository: https://github.com/apnadkarni/tcl-sfe
Docs: https://github.com/apnadkarni/tcl-sfe/blob/main/README.md
This repository hosts single file executables (SFE) of Tcl/Tk for
Windows and a build system to create and customize them. These are
completely statically linked (other than the C and Windows runtimes) and >>> therefore avoid issues that arise from similar single file executables
that write shared libraries to disk.
The `tclsfe.exe` and `tksfe.exe` programs are enhanced versions of
`tclsh.exe` and `wish.exe` that include the following extensions by
default:
- thread
- sqlite3
- tdbc::odbc
- twapi
The build system allows omission of any of these if smaller executables
are desired. Further, SFE's can be customized by adding
- additional packages or Tcl modules
- interpreter initalization scripts
- a main script that implements a complete application
- statically linked extensions
Only the last customization needs a compiler.
Downloads: https://github.com/apnadkarni/tcl-sfe/releases
Repository: https://github.com/apnadkarni/tcl-sfe
Docs: https://github.com/apnadkarni/tcl-sfe/blob/main/README.md
This repository hosts single file executables (SFE) of Tcl/Tk for
Windows and a build system to create and customize them. These are
completely statically linked (other than the C and Windows runtimes) and therefore avoid issues that arise from similar single file executables
that write shared libraries to disk.
The `tclsfe.exe` and `tksfe.exe` programs are enhanced versions of `tclsh.exe` and `wish.exe` that include the following extensions by
default:
- thread
- sqlite3
- tdbc::odbc
- twapi
The build system allows omission of any of these if smaller executables
are desired. Further, SFE's can be customized by adding
- additional packages or Tcl modules
- interpreter initalization scripts
- a main script that implements a complete application
- statically linked extensions
Only the last customization needs a compiler.
v0.2 release
- added x86 binaries
- -icon option for modifying the icon
- -version option for customizing version resource (version, company and product name etc.)
Eric, I thought about also adding your code. But I still see a potential for conflicts. For example, the new SFE might contain a newer version of a package. Copying missing entries from an older SFE may copy older versions of the package (because directories are versioned by many packages) creating a runtime conflict. It would be a useful feature but have to think about it a bit more.
/Ashok
On 7/4/2026 8:32 AM, Ashok wrote:
Downloads: https://github.com/apnadkarni/tcl-sfe/releases
Repository: https://github.com/apnadkarni/tcl-sfe
Docs: https://github.com/apnadkarni/tcl-sfe/blob/main/README.md
This repository hosts single file executables (SFE) of Tcl/Tk for
Windows and a build system to create and customize them. These are
completely statically linked (other than the C and Windows runtimes) and
therefore avoid issues that arise from similar single file executables
that write shared libraries to disk.
The `tclsfe.exe` and `tksfe.exe` programs are enhanced versions of
`tclsh.exe` and `wish.exe` that include the following extensions by
default:
- thread
- sqlite3
- tdbc::odbc
- twapi
The build system allows omission of any of these if smaller executables
are desired. Further, SFE's can be customized by adding
- additional packages or Tcl modules
- interpreter initalization scripts
- a main script that implements a complete application
- statically linked extensions
Only the last customization needs a compiler.
The icon is a nice addition.
Ashok,
The icon is a nice addition.
Fair point on my -exe idea. What if instead, we just allow an export -
the code below
is roughly what I'm thinking:
This sidesteps the versioning problem you raised entirely - nothing the
tool has to get right on the person's behalf. Usage would be something
like:
-a-a-a sfe::export oldrelease.exe C:/staging/old_packages
Here's how that might then be re-imported after the user goes over the directory and removes anything stale or unwanted - maybe with a new
-allof option so they don't have to glob it themselves:
-a-a-a sfe::make newrelease.exe -allof C:/staging/old_packages
proc sfe::export {exePath outDir {force ""}} {
-a-a-a # Extracts the package content of an SFE to a plain directory on
-a-a-a # disk, so specific packages can be inspected and selectively fed
-a-a-a # back into sfe::make. outDir must not already exist, or must be
-a-a-a # empty, unless force is "force".
-a-a-a set exePath [file normalize $exePath]
-a-a-a if {![file isfile $exePath]} {
-a-a-a-a-a-a-a error "\"$exePath\" is not a file."
-a-a-a }
-a-a-a if {[string tolower [file extension $exePath]] ne ".exe"} {
-a-a-a-a-a-a-a error "\"$exePath\" does not have a .exe extension."
-a-a-a }
-a-a-a set outDir [file normalize $outDir]
-a-a-a if {[file exists $outDir]} {
-a-a-a-a-a-a-a if {![file isdirectory $outDir]} {
-a-a-a-a-a-a-a-a-a-a-a error "\"$outDir\" exists and is not a directory."
-a-a-a-a-a-a-a }
-a-a-a-a-a-a-a if {$force ne "force" && [llength [glob -nocomplain -directory
$outDir *]]} {
-a-a-a-a-a-a-a-a-a-a-a error "\"$outDir\" is not empty. Use \"force\" to reuse it
anyway."
-a-a-a-a-a-a-a }
-a-a-a } else {
-a-a-a-a-a-a-a file mkdir $outDir
-a-a-a }
-a-a-a set mnt //zipfs:/sfeexport_[pid]
-a-a-a zipfs mount $exePath $mnt
-a-a-a try {
-a-a-a-a-a-a-a file copy -force -- {*}[glob [file join $mnt *]] $outDir
-a-a-a } finally {
-a-a-a-a-a-a-a zipfs unmount $mnt
-a-a-a }
}
Eric
On 7/7/2026 11:33 PM, Ashok wrote:
v0.2 release
- added x86 binaries
- -icon option for modifying the icon
- -version option for customizing version resource (version, company
and product name etc.)
Eric, I thought about also adding your code. But I still see a
potential for conflicts. For example, the new SFE might contain a
newer version of a package. Copying missing entries from an older SFE
may copy older versions of the package (because directories are
versioned by many packages) creating a runtime conflict. It would be a
useful feature but have to think about it a bit more.
/Ashok
On 7/4/2026 8:32 AM, Ashok wrote:
Downloads: https://github.com/apnadkarni/tcl-sfe/releases
Repository: https://github.com/apnadkarni/tcl-sfe
Docs: https://github.com/apnadkarni/tcl-sfe/blob/main/README.md
This repository hosts single file executables (SFE) of Tcl/Tk for
Windows and a build system to create and customize them. These are
completely statically linked (other than the C and Windows runtimes) and >>> therefore avoid issues that arise from similar single file executables
that write shared libraries to disk.
The `tclsfe.exe` and `tksfe.exe` programs are enhanced versions of
`tclsh.exe` and `wish.exe` that include the following extensions by
default:
- thread
- sqlite3
- tdbc::odbc
- twapi
The build system allows omission of any of these if smaller executables
are desired. Further, SFE's can be customized by adding
- additional packages or Tcl modules
- interpreter initalization scripts
- a main script that implements a complete application
- statically linked extensions
Only the last customization needs a compiler.
| Sysop: | Amessyroom |
|---|---|
| Location: | Fayetteville, NC |
| Users: | 74 |
| Nodes: | 6 (0 / 6) |
| Uptime: | 50:10:00 |
| Calls: | 1,100 |
| Files: | 1,339 |
| Messages: | 275,859 |