- Inno Setup on Linux and macOS
[[http://www.jrsoftware.org/isinfo.php][Inno Setup]] is a popular installer builder for Windows. Of course it is made to run on Windows only, by default. But what if you want to build Windows installers /off/ Windows, i.e. on Linux or macOS?
You're in luck: It's possible to run Inno Setup anywhere that [[https://www.docker.com/][Docker]] runs (including Linux and macOS), and even have a passable experience writing your setup script.
** Containerized compiler
To run Inno Setup outside of Windows we will construct a Tower of Babel: install Inno Setup in [[https://www.winehq.org/][Wine]] in a Docker container.
First, get Docker without needing to make an account:
- [[https://docs.docker.com/docker-for-windows/install/#install-docker-desktop-for-windows-desktop-app][Docker Desktop for Windows]]
- [[https://docs.docker.com/docker-for-mac/release-notes/][Docker Desktop for Mac]]
- [[https://docs.docker.com/install/#supported-platforms][Docker CE for Linux]]
Then you can run the command-line Inno Setup compiler, [[http://www.jrsoftware.org/ishelp/topic_compilercmdline.htm][iscc]], like so (assuming you have a setup script called =helloworld.iss=):
#+begin_src shell docker run --rm -i -v "$PWD:/work" amake/innosetup helloworld.iss #+end_src
You can even put iscc on your PATH by wrapping this up in a script, named
iscc of course:
#+begin_src shell #!/usr/bin/env bash
exec docker run --rm -i -v "$PWD:/work" amake/innosetup "$@" #+end_src
See [[https://github.com/amake/dotfiles/blob/9985db064949ef29e42029674bdbdd605f01d57b/bin/bin/iscc][here]] for a more advanced wrapper script.
Note that there are some limitations inherent in running the compiler in a container:
- The setup script will not be able to "see" any files outside of the directory
mounted into the Docker container (
$PWDin the above scripts), so some care is needed to ensure all referenced files are accessible. - I have been unable to get code signing of payload files (
SignToolsettings) to work. See the Code Signing section below for signing the installer itself.
** Setup script
Creating or modifying an Inno Setup setup script can be a pain (when's the last time you wrote any Pascal?). To make life a little bit easier, I made an Emacs linter for setup scripts: [[https://github.com/amake/.emacs.d/blob/3f386a208e96461beecf650ff0fb7bf66b8f56ce/lisp/flycheck-innosetup.el][flycheck-innosetup.el]].
Here's a sample config using [[https://github.com/jwiegley/use-package][use-package]]:
#+begin_src elisp (use-package flycheck :config (global-flycheck-mode))
(use-package flycheck-innosetup
:after flycheck
;; Point :load-path' to wherever you put flycheck-innosetup.el. You can ;; probably also pull directly from git with ;;
https://github.com/raxod502/straight.el'
:load-path "lisp"
:ensure nil
:config
(flycheck-innosetup-setup))
(use-package pascal :ensure nil :mode (("\.iss\'" . pascal-mode))) #+end_src
This will get you inline error highlighting, though it can be quite slow if your script references a lot of files.
** Code signing
You can sign the installer itself with [[https://sourceforge.net/projects/osslsigncode/files/osslsigncode/][osslsigncode]], which is probably available in your package manager. I have also [[https://github.com/omegat-org/osslsigncode-docker][containerized]] it; a wrapper script can be made in much the same way as for the compiler above.
** Continuous integration
A key use case for all of this is to build Windows installers in CI, where you are likely to be running Linux.
One issue to be aware of is that Docker might be running under a different user
and may not be able to read your checkout files or write to output
directories. Judicious use of umask and/or chmod can remedy this.
Happy installing!