CINXE.COM
Tcl/Tk application binaries through Go
<!DOCTYPE html> <html lang='en'> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width, initial-scale=1'> <meta name='description' content='Tclers wiki'> <meta name='author' content=''> <link rel='icon' href='/img/favicon.ico'> <title>Tcl/Tk application binaries through Go</title> <!-- Latest compiled and minified CSS --> <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'> <link rel='stylesheet' href='/css/nikit.css' type='text/css'> <link rel='stylesheet' href='/css/sh_style.css' type='text/css'> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.0/css/theme.bootstrap_3.min.css' type='text/css'> <script src='//cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js'></script> </head> <body onload='sh_highlightDocument(); nikitUser();'> <nav class='navbar navbar-fixed-top navbar-inverse'> <div class='container'> <div class='navbar-header'> <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='#myNavbar'> <span class='icon-bar'></span> <span class='icon-bar'></span> <span class='icon-bar'></span> </button> <ul class='nav navbar-nav'> <li class='dropdown'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'> Tcler's Wiki<span class='caret'></span></a> <ul class='dropdown-menu scrollable-menu' role='menu'><li><a href='/welcome'>Home</a></li> <li><a rel='nofollow' href='/recent'>Changes</a></li> <li><a rel='nofollow' href='/_random'>Random page</a></li> <li><a rel='nofollow' href='/_new'>New page</a></li> </ul> </li> </ul> </div> <div class='collapse navbar-collapse' id='myNavbar'> <ul class='nav navbar-nav'> <li class='dropdown' id='li_idPageEdit' style='display:none'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'><span id=name_idPageEdit>Page</span><span class='caret'></span></a> <ul class='dropdown-menu scrollable-menu' role='menu'><li><a rel='nofollow' href='/_edit/Tcl%2FTk+application+binaries+through+Go?A=1'>Comment</a></li> <li><a rel='nofollow' hidden='true' href='/_edit/Tcl%2FTk+application+binaries+through+Go'>Edit</a></li> <li><a rel='nofollow' href='/_upload/Tcl%2FTk+application+binaries+through+Go'>Upload</a></li> <li><a rel='nofollow' href='/ref/Tcl%2FTk+application+binaries+through+Go'>References</a></li> <li><a rel='nofollow' href='/history/Tcl%2FTk+application+binaries+through+Go'>History</a></li> <li><hr></li> <li><a href='#3bee588006fc123d8649a247e707336b6e414bc01d4ed81e6bdc5037064e163e'> Example </a> </li> <li><a href='#b73493522419c1ad5c5be1b7514c6fbcbb56bdc7c71f00a8053f56e87b4e7aaf'> Limitations and quirks </a> </li> <li><a href='#f0d0837119d2e68a0faac82afcb4df3df16c9b91e1f5f13351601d6cf748612c'> Discussion </a> </li> <li><a href='#07679025be2a0b218e5c015f655cd7f8e7769d8eed2604c8faf6c857e5b2a953'> See also </a> </li> </ul> </li> <li class='dropdown' id='li_idPageNoEdit' style='display:none'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'><span id=name_idPageNoEdit>Page</span><span class='caret'></span></a> <ul class='dropdown-menu scrollable-menu' role='menu'><li><a rel='nofollow' href='/ref/Tcl%2FTk+application+binaries+through+Go'>References</a></li> <li><a rel='nofollow' href='/history/Tcl%2FTk+application+binaries+through+Go'>History</a></li> <li><hr></li> <li><a href='#3bee588006fc123d8649a247e707336b6e414bc01d4ed81e6bdc5037064e163e'> Example </a> </li> <li><a href='#b73493522419c1ad5c5be1b7514c6fbcbb56bdc7c71f00a8053f56e87b4e7aaf'> Limitations and quirks </a> </li> <li><a href='#f0d0837119d2e68a0faac82afcb4df3df16c9b91e1f5f13351601d6cf748612c'> Discussion </a> </li> <li><a href='#07679025be2a0b218e5c015f655cd7f8e7769d8eed2604c8faf6c857e5b2a953'> See also </a> </li> </ul> </li> <li><a href="/page/Showcase">Showcase</a></li> <li><a href="/page/Tcl+Tutorial+Lesson+0">Tutorial</a></li> <li><a href="/page/Articles">Articles</a></li> <li><a href="/page/Tcl+Playground">Playground</a></li> <li class='dropdown'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'> Help<span class='caret'></span></a> <ul class='dropdown-menu scrollable-menu' role='menu'><li><a rel='nofollow' href='/page/Help'>Page Markup</a></li> <li><a rel='nofollow' href='/page/How+do+Wiki+Categories+work'>Wiki Categories</a></li> <li><a rel='nofollow' href='/page/Contents'>Topics</a></li> <li><a rel='nofollow' target='_blank' href='https://chiselapp.com/user/stevel/repository/nikit/ticket'>Report Problems</a></li> <li><a rel='nofollow' href='/privacy'>Privacy</a></li> <li><a rel='nofollow' href='/license'>License</a></li> </ul> </li> </ul> <ul class='nav navbar-nav navbar-right'> <li class='dropdown'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'><span id=name_SMenu>User</span><span class='caret'></span></a> <ul class='dropdown-menu' id='ul_SMenu'> </ul> </li> </ul> <form class='navbar-form navbar-right' method='post' action='/search' id='searchform'> <input name='Q' type='text' class='form-control' placeholder='Search...'/> <input type="hidden" name="sites" value="wiki.tcl-lang.org"/> </form> </div> </div> </nav> <div class='container'> <div class='row'> <div class='col-xs-12'> <h2>Tcl/Tk application binaries through Go</h2> </div> </div> <div class='row'> <div class='col-xs-12'> <p class='mkup_p'>2025-02-24 <a class='mkup_a mkup_known' href='/page/dbohdan'>dbohdan</a>: I have discovered <a rel='nofollow' class='mkup_a' href='https://pkg.go.dev/modernc.org/tk9.0'>modernc.org/tk9.0 <span class='glyphicon glyphicon-globe' aria-hidden='true'></span></a>. This remarkable <a class='mkup_a mkup_known' href='/page/Go'>Go</a> package is an automatic translation of Tcl/Tk 9.0 from C to pure Go. Its primary goal is to allow Go developers to use Tk without C library dependencies. However, because the binaries include a full Tcl 9 interpreter, they can run arbitrary Tcl code. When you combine this with the package <a rel='nofollow' class='mkup_a' href='https://pkg.go.dev/embed'>embed <span class='glyphicon glyphicon-globe' aria-hidden='true'></span></a> in Go's standard library, it gives you a means to:</p><UL class='mkup_UL'><li class='mkup_li'>Turn Tcl code into standalone executables</li><li class='mkup_li'>Easily cross-compile these executables for other systems</li></UL><p class='mkup_p'>This is something I think Tclers should know about and could make use of.</p> <h2 id='3bee588006fc123d8649a247e707336b6e414bc01d4ed81e6bdc5037064e163e' class='mkup_h1'> Example </h2><p class='mkup_p'>In the following example, we are going to turn a single Tcl source file into a binary. Before we start, we need to install a recent version of Go. The example has been tested with Go 1.22 on Ubuntu 24.04 (x86_64).</p><p class='mkup_p'>We are going to use the unaltered code of the <a class='mkup_a mkup_known' href='/page/Tiny+Excel%2Dlike+app+in+plain+Tcl%2FTk'>Tiny Excel-like app in plain Tcl/Tk</a> as <span class='mkup_tt'>main.tcl</span>. Create a new directory called <span class='mkup_tt'>test</span> and save that code as <span class='mkup_tt'>main.tcl</span> in the directory.</p><p class='mkup_p'>Now we must create a Go wrapper for our program. Save the following code as <span class='mkup_tt'>main.go</span> in the same directory as <span class='mkup_tt'>main.tcl</span>.</p><div class='sh_sourceCode'><button class='copybtn btn pull-right' data-clipboard-target='#mkup_code_0' title='Click to copy code snippet to clipboard'><span class='glyphicon glyphicon-copy' aria-hidden='true'></span></button><pre id='mkup_code_0' class='mkup_pre sh_sourceCode'>package main import ( _ "embed" . "modernc.org/tk9.0" . "modernc.org/tk9.0/extensions/eval" _ "modernc.org/tk9.0/themes/azure" ) //go:embed main.tcl var tclMain string func main() { InitializeExtension("eval") ActivateTheme("azure light") Eval(tclMain) App.Center().Wait() }</pre></div><p class='mkup_p'>The code uses the directive <span class='mkup_tt'>//go:embed</span> to store the contents of <span class='mkup_tt'>main.tcl</span> in the variable <span class='mkup_tt'>tclMain</span> at build time. It imports the <a rel='nofollow' class='mkup_a' href='https://github.com/rdbende/Azure-ttk-theme'>Azure <span class='glyphicon glyphicon-globe' aria-hidden='true'></span></a> <a class='mkup_a mkup_known' href='/page/List+of+ttk+Themes'>Tk theme</a>. modernc.org/tk9.0 currently provides this one custom theme in addition to standard themes like Clam. The theme looks like Microsoft's <a rel='nofollow' class='mkup_a' href='https://en.wikipedia.org/wiki/Fluent_Design_System'>Fluent Design <span class='glyphicon glyphicon-globe' aria-hidden='true'></span></a> on all systems and implements <a class='mkup_a mkup_unknown' href='/_new?newpagename=dark+mode'>dark mode</a>.</p><p class='mkup_p'>With the wrapper in place, let's run the commands to initialize the Go module (project) and download the dependencies. (Run them in the directory with <span class='mkup_tt'>main.go</span> and <span class='mkup_tt'>main.tcl</span>.)</p><div class='sh_sourceCode'><button class='copybtn btn pull-right' data-clipboard-target='#mkup_code_1' title='Click to copy code snippet to clipboard'><span class='glyphicon glyphicon-copy' aria-hidden='true'></span></button><pre id='mkup_code_1' class='mkup_pre sh_sourceCode'>go mod init test go mod tidy</pre></div><p class='mkup_p'>This will create the files <span class='mkup_tt'>go.mod</span> and <span class='mkup_tt'>go.sum</span>. In a real project, you should commit both to source control.</p><p class='mkup_p'>Now we have all we need to build and run the program. Let's do it.</p><div class='sh_sourceCode'><button class='copybtn btn pull-right' data-clipboard-target='#mkup_code_2' title='Click to copy code snippet to clipboard'><span class='glyphicon glyphicon-copy' aria-hidden='true'></span></button><pre id='mkup_code_2' class='mkup_pre sh_sourceCode'>go build -trimpath # POSIX shell. ./test # PowerShell or cmd.exe on Windows. .\test.exe</pre></div><p class='mkup_p'>Once <span class='mkup_tt'>test</span> starts, you should see something a lot like this screenshot (minus the formulas in the cells):</p><p class='mkup_p'><img class='mkup_img' alt='modernc.org-tk9.0-test.png' src='/image/modernc%2Eorg%2Dtk9%2E0%2Dtest%2Epng'></p><p class='mkup_p'>The flag <span class='mkup_tt'>-trimpath</span> excludes our full build paths from the executable for reproducible builds and privacy. The final binary is 8.3 MiB when compiled with Go 1.22 on x86_64 Ubuntu 24.04.</p><p class='mkup_p'>We can also leverage Go's cross-compilation. It takes a single command to build the same program for Windows.</p><div class='sh_sourceCode'><button class='copybtn btn pull-right' data-clipboard-target='#mkup_code_3' title='Click to copy code snippet to clipboard'><span class='glyphicon glyphicon-copy' aria-hidden='true'></span></button><pre id='mkup_code_3' class='mkup_pre sh_sourceCode'>GOOS=windows go build -trimpath</pre></div><p class='mkup_p'>The resulting AMD64 Windows executable is 8.9 MiB. Note that it displays a console window in addition to the GUI.</p><p class='mkup_p'>If you are on Windows, you can cross-compile for Linux:</p><div class='sh_sourceCode'><button class='copybtn btn pull-right' data-clipboard-target='#mkup_code_4' title='Click to copy code snippet to clipboard'><span class='glyphicon glyphicon-copy' aria-hidden='true'></span></button><pre id='mkup_code_4' class='mkup_pre sh_sourceCode'>rem cmd.exe set GOOS=linux go build -trimpath</pre></div><div class='sh_sourceCode'><button class='copybtn btn pull-right' data-clipboard-target='#mkup_code_5' title='Click to copy code snippet to clipboard'><span class='glyphicon glyphicon-copy' aria-hidden='true'></span></button><pre id='mkup_code_5' class='mkup_pre sh_sourceCode'># PowerShell $env:GOOS="linux" go build -trimpath</pre></div> <h2 id='b73493522419c1ad5c5be1b7514c6fbcbb56bdc7c71f00a8053f56e87b4e7aaf' class='mkup_h1'> Limitations and quirks </h2><p class='mkup_p'>By default, Linux binaries aren't static and depend on libc. You can override the linker options to build a static binary. You may want to build static binaries on a Linux system with <a class='mkup_a mkup_unknown' href='/_new?newpagename=musl'>musl</a> libc, not <a class='mkup_a mkup_unknown' href='/_new?newpagename=glibc'>glibc</a>.</p><p class='mkup_p'>Links for more information:</p><UL class='mkup_UL'><li class='mkup_li'><a rel='nofollow' class='mkup_a' href='https://www.arp242.net/static-go.html'>"Statically compiling Go programs" <span class='glyphicon glyphicon-globe' aria-hidden='true'></span></a>, Martin Tournoij ("arp242")</li><li class='mkup_li'><a rel='nofollow' class='mkup_a' href='https://stackoverflow.com/questions/57476533/why-is-statically-linking-glibc-discouraged'>"Why is statically linking glibc discouraged?" <span class='glyphicon glyphicon-globe' aria-hidden='true'></span></a> on Stack Overflow</li></UL><h2 id='f0d0837119d2e68a0faac82afcb4df3df16c9b91e1f5f13351601d6cf748612c' class='mkup_h1'> Discussion </h2> <h2 id='07679025be2a0b218e5c015f655cd7f8e7769d8eed2604c8faf6c857e5b2a953' class='mkup_h1'> See also </h2><UL class='mkup_UL'><li class='mkup_li'><a rel='nofollow' class='mkup_a' href='https://modernc.org/sqlite'>modernc.org/sqlite <span class='glyphicon glyphicon-globe' aria-hidden='true'></span></a> — a Go translation of <a class='mkup_a mkup_known' href='/page/SQLite'>SQLite</a></li></UL><hr><div class='mkup_centered'><table class='mkup_categories'><tr><td class='mkup_td'><a class='mkup_a' href='/page/Category+Deployment'>Category Deployment</a></td><td class='mkup_td'><a class='mkup_a' href='/page/Category+Standalone+Executables'>Category Standalone Executables</a></td></tr></table></div> </div> </div> <div class='row'> <div class='col-xs-12'> <div class='Footer'>Updated 2025-02-25 09:05:37</div> </div> </div> </div> <!-- jQuery library --> <script src='https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.0/js/jquery.tablesorter.combined.js'></script> <!-- Latest compiled JavaScript --> <script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script> <script type='text/javascript' src='/scripts/nikit.js'></script> <script type='text/javascript' src='/scripts/sh_main.js'></script> <script type='text/javascript' src='/scripts/sh_tcl.js'></script> <script type='text/javascript' src='/scripts/sh_c.js'></script> <script type='text/javascript' src='/scripts/sh_cpp.js'></script> <!-- <script src='https://www.google.com/recaptcha/api.js'></script> --> <script src='https://hcaptcha.com/1/api.js'></script> <script>var clipboard = new ClipboardJS('.copybtn', { text: function(trigger) { return document.querySelector(trigger.getAttribute('data-clipboard-target')).textContent + '\n'; } }); sort_tables(); </script> </body> </html>