Skip to content

Instantly share code, notes, and snippets.

@amake
Last active April 17, 2024 13:34
Show Gist options
  • Star 56 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save amake/3e7194e5e61d0e1850bba144797fd797 to your computer and use it in GitHub Desktop.
Save amake/3e7194e5e61d0e1850bba144797fd797 to your computer and use it in GitHub Desktop.
Inno Setup on Linux and macOS

Inno Setup on Linux and macOS

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 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 Wine in a Docker container.

First, get Docker without needing to make an account:

Then you can run the command-line Inno Setup compiler, iscc, like so (assuming you have a setup script called helloworld.iss):

docker run --rm -i -v "$PWD:/work" amake/innosetup helloworld.iss

You can even put iscc on your PATH by wrapping this up in a script, named iscc of course:

#!/usr/bin/env bash

exec docker run --rm -i -v "$PWD:/work" amake/innosetup "$@"

See 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 ($PWD in 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 (SignTool settings) 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: flycheck-innosetup.el.

Here’s a sample config using use-package:

(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)))

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 osslsigncode, which is probably available in your package manager. I have also included it in the amake/innosetup Docker image; 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!

@levymikael
Copy link

Hello

Do you have an alternative to Wine, because Wine is running on a 32-bit environment which Mac OS Catalina doesn't (64-bit)?
Thank you

@amake
Copy link
Author

amake commented Aug 1, 2020

For general use, no. For the use case described in this article, running Wine in Docker works fine anywhere Docker runs (including Catalina).

@jkoplo
Copy link

jkoplo commented Feb 16, 2022

This a beautiful, abhorrent monstrosity. 🙏

@mrmachine
Copy link

Have you tried to use this for signtool? It claims to call osslsigncode on the host.

https://github.com/dustinblackman/mono-signtool

But when I try it just hangs without any error. Perhaps you can spot a quick fix, as it is quite old and archived.

I am currently using osslsigncode to sign my app before packaging and then inno setup to sign the installer, but I think there is no way for me to sign the installer that inno setup is including in the package.

@amake
Copy link
Author

amake commented Mar 12, 2022

@mrmachine Thanks for pointing that out. I've tried to do basically the same thing but wrapping osslsigncode with bash, and it seems like it should work but it just doesn't.

The best I've gotten is

  1. Add the line SignTool=mysigntool to the [Setup] block of your .iss file

  2. Run this:

    docker run -it --rm -v $PWD:/work amake/innosetup:64bit '/Smysigntool=cmd /c for /f %f in ('\''winepath -u $f'\'') do start /wait /unix /bin/bash -c $qcp %f /tmp/foo && /usr/bin/osslsigncode $$@ && cp /tmp/foo-signed %f$q _ sign -pkcs12 /work/cert.pfx -pass pass -t http://timestamp.sectigo.com -h sha2 -in /tmp/foo -out /tmp/foo-signed' myiss.iss
    

    (osslsigncode refuses to sign the file in-place, hence the temp files)

But the result is like the following with no additional info:

Error in Z:\work\myiss.iss: Sign Tool command failed with exit code 0x7BCE4C9C.

I'm stuck.

@mrmachine
Copy link

@amake I am actually using Crossover to run InnoSetup (before I discovered your containerized version).

I tried to manually run a bash script from an interactive WINE terminal (in Crossover, right click on the bottle, hold option and click Open Shell) via cmd /c start /unix /path/to/bashscript.sh and the bash script does execute.

It was just a simple test which pipes some output to a new file. The new file gets created. So I agree, in theory it should be possible to wrap osslsigncode with a bash script and call that from InnoSetup (or generally, from WINE).

Can you try to run your osslsigncode bash script via WINE interactive shell, instead of via InnoSetup? It may report a more useful error that InnoSetup is swallowing?

@trieska
Copy link

trieska commented Aug 23, 2022

hi,
I find this very helpfull container. Thank you!
but
running it from jenkins is not easy, well in my case I am getting file with UID=1000 and GID=101 and with 644 permision and jenkins can not remove it when cleaning job ...
before I start this container I am doing chmod 777 on dir which I am mapping to container, so inno can write there.
I did also run umask 0000 before start container but it did not help.

Please, can you run umask inside of container? it will be very usefull
Please, where can I find dockfire for this container?

thank you very much

@trieska
Copy link

trieska commented Aug 23, 2022

looks that I did overlook that this is gist ...
so I did find https://github.com/amake/innosetup-docker/blob/latest/Dockerfile
and I did understand how inno is started

so I did build my image using this dockerfile

FROM amake/innosetup
COPY ./entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

and this is entrypoint.sh

#!/bin/bash
umask 0000
iscc "$@"

and before running container I am running
chmod 777 WORK_DIR

hura, outputfile has 777 permisions

@igorespin
Copy link

Any ideas to run inno setup over Apple M1 or M2?

@mrmachine
Copy link

@amake Just returning to this now. I am not able to run your Inno Setup docker image on Apple Silicon for some reason, but I was able to get Inno Setup installed via CrossOver (32-bit Windows 7 bottle) to use osslsigncode on the macOS host as a sign tool.

I think the primary issue is that cmd /c start /wait ... does not actually wait if the command launches a new process, which confused Inno Setup because the sign tool returns 0 but the file is not yet signed.

I couldn't find any way around that. I tried calling /usr/local/bin/osslsigncode directly and wrapped in .sh and .bat files. But when I added 15 seconds of pointless busywork to my .bat file, that delayed the return long enough for Inno Setup to find the signed file and be happy.

osslsigncode.bat

@echo off

@REM Sign code from CrossOver/WINE via osslsigncode on macOS.
@REM First, `brew install osslsigncode` on macOS.

@REM USAGE: osslsigncode.bat <PASSWORD> <FILE>

set PASSWORD=%~1

@REM Get unix path for input file.
for /f "usebackq tokens=* " %%i in (`winepath -u %~2`) do set FILE=%%i
echo FILE: %FILE%
echo.

@REM Temporary output file. Inline signing does not work.
set SIGNED=%FILE%.signed
echo SIGNED: %SIGNED%
echo.

@REM Delete existing temporary output file.
del /f "%SIGNED%" > nul 2>&1

@REM Get unix path for project directory.
for /f "usebackq tokens=* " %%i in (`winepath -u %~dp0`) do set PROJECT_DIR=%%i
echo PROJECT_DIR: %PROJECT_DIR%
echo.

echo CMD: start /wait /unix /usr/local/bin/osslsigncode sign -pkcs12 "%PROJECT_DIR%/cert.p12" -pass "..." -i https://example.com -t http://timestamp.digicert.com -in "%FILE%" -out "%SIGNED%"
echo.
start /wait /unix /usr/local/bin/osslsigncode sign -pkcs12 "%PROJECT_DIR%/cert.p12" -pass "%PASSWORD%" -i https://example.com -t http://timestamp.digicert.com -in "%FILE%" -out "%SIGNED%"

@REM `start /wait` above does not seem to wait, so we need to explicitly wait here.
echo sleep for 15 seconds...
echo.
ping 127.0.0.1 -n 15 > nul 2>&1

@REM Replace input file with signed output file.
move /y "%SIGNED%" "%FILE%" -out "%

And how I configure Inno Setup:

[Setup]
SignTool=osslsigncode $f

And how I run Inno Setup via CLI from a CrossOver command prompt:

"C:\Program Files\Inno Setup 6\iscc.exe" /Qp /S"osslsigncode=%~dp0\osslsigncode.bat %PASSWORD% $p" inno-setup.iss

@Steffen-MLR
Copy link

@mrmachine @amake The solution depends on wine running unix commands, which is due to various parts not the best way (like ping to wait...)..

There is indeed a windows-32bit version of osslsigncode. I rewrote your script @mrmachine to work with the windows version, that can be executed in wine "natively" and integrates pretty nice.

I added this to my repo including a little information on how to use it:

docker run --rm -i -v $PWD:/work amake/innosetup "your-installer-file.iss" '/Ssigntool=Z:\opt\osslsigncode.bat <PASSWORD> <PATH_TO_PFX> https://example.com $f'

https://github.com/Steffen-MLR/innosetup-docker/blob/daa4ed3a161ac74c1471b90123bf5b23befbc537/authenticate.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment