Introduction to the Windows software I needed: FORScan
I recently noticed that my 2013 Ford Transit mk7 van doesn’t have great observability to debug what went wrong and it just shows
ENGINE MALFUNCTION
text in the small screen instead of actually providing helpful error messages. This message is pretty scary, but my car seemed to move forward so I thought it’s mainly to get more money for the Ford service shops.I had learned that it’s pretty easy to diagnose these errors by installing FORScan for my machine and finding compatible OBD2 cable. The only problem was that FORScan only works with Windows machines and I only had Macbook available.
Figuring out how to run FORScan on MacOS
I spend sometime googling how I could get FORScan working on my Macbook and stumbled upon this great discussion in miata.net car enthusiastics. I learned that I would need to:
- Install Wine through Homebrew
- Find proper device drivers for my USB OBD2 cable
- Symlinking the connected USB serial devices to Wine COM ports
I followed their great instructions and was able to get FORScan working but this seemed very tedious way to set it up and I wondered if all of this could be automated somehow with Homebrew so that it would be easier to setup next time for the benefit of myself and others.
Automating FORScan installation steps with Homebrew
I had noticed that Homebrew supports custom taps. Taps are basically 3rd party packages which anyone can create and host in their public github repository called
homebrew-tap
.Everything tap related is very well documented by Homebrew: https://docs.brew.sh/Formula-Cookbook.
In order to create a new tap you need to create a git repository called
homebrew-tap
with folder Casks
# Create new homebrew tap which will host your casks formulaes YOUR_GITHUB_USERNAME="onnimonni" # change this to your username! brew tap-new $YOUR_GITHUB_USERNAME/homebrew-tap # Change directory to the newly created tap. You can see this from the output of the earlier command cd /opt/homebrew/Library/Taps/$YOUR_GITHUB_USERNAME/homebrew-tap
Next I needed to find the URL for the installer. This was easy to do by just right-clicking the installer link and using
Copy Link Address
feature in Chrome. In my case the installer link was: With the link to the installer I was able create a new cask for Homebrew using their template:
brew create --tap $YOUR_GITHUB_USERNAME/homebrew-tap \ --set-name FORScan \ --cask "https://FORScan.org/download/FORScanSetup2.3.62.release.exe"
This opens your code editor and automatically fills up the the name and is able to figure out the version and the checksum automatically:
# Documentation: https://docs.brew.sh/Cask-Cookbook # https://docs.brew.sh/Adding-Software-to-Homebrew#cask-stanzas # PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST! cask "FORScanni" do version "2.3.62" sha256 "fad11dd5f91a86961b2435798eeae8241dd3a69c92808f5b9eedfe35eac2a77c" url "https://FORScan.org/download/FORScanSetup#{version}.release.exe" name "FORScanni" desc "" homepage "" # Documentation: https://docs.brew.sh/Brew-Livecheck livecheck do url "" strategy "" end depends_on macos: "" app "" # Documentation: https://docs.brew.sh/Cask-Cookbook#stanza-zap zap trash: "" end
Well this is nice. Next I added proper values for
desc
and homepage
. I also added auto_updates false
line into the formula to tell homebrew that this software can’t automatically update itself. If you’re unsure please check guidance from Homebrew docs.Automatically updating Casks by adding livecheck block
livecheck
is needed if you want to be able to easily update the version with Homebrew. I needed to use a bit time to figure this out but FORScan change history page provides links to all versions https://FORScan.org/changes_history.html and I noticed that all download links follow this pattern download/FORScanSetup
.Searching examples with Github code search helped me to figure out how to use regex properly here:
livecheck do url "https://FORScan.org/changes_history.html" regex(%r{href=.*download/FORScanSetup(\d+(?:\.\d+)+)\.release\.exe}) end
Adding other casks as dependencies to your cask
Next I needed to add additional dependencies which we will need in order to get my software running. If your windows software will not need USB serial devices you should not need anything else than
wine-stable
.# Since FORScan is made for Windows we need to install Wine to run it depends_on cask: "wine-stable" # Virtual COM Drivers which work for vLinker FS USB OBD2 reader # They also should work for the OBDLink EX which is the other recommended cable # These are needed for the MacOS host machine to detect the OBD2 reader depends_on cask: "ftdi-vcp-driver"
Running the downloaded Windows installer
Remember that Windows software typically doesn’t look like the self-contained
Application.app
containers we have on MacOS? With Windows you will download a installer which will then create a shortcut to desktop and add the software to your C:/Program Files (x86)/FORScan/FORScan.exe
. We have now only told the Homebrew to download this installer.We can run the installer manually by locating it and running that with Wine:
wine FORScanSetup2.3.62.release.exe
But this doesn’t look very automatic and doesn’t happen in the background. You can ask for Windows installer flags by providing
/help
as the third parameter:wine FORScanSetup2.3.62.release.exe /help
Running this command will open a new long window containing all of the different installer options. I thought that I will not want to change the default installation path but I just want it to use defaults and run in the background. By adding following flags I was able to do that:
wine FORScanSetup2.3.62.release.exe /SP- /VERYSILENT
This will run the installer for us and adds the FORScan program files into
$HOME/.wine/drive_c/Program Files (x86)/FORScan/FORScan.exe
. We can now add a Homebrew installer
keyword into our formula. See how I used the staged_path
and version
variables provided by Homebrew.# Runs the FORScan Windows installer installer script: { executable: "wine", args: [ "#{staged_path}/FORScanSetup#{version}.release.exe", # Windows EXE installer options here: # Run the FORScan without user input "/SP-", "/VERYSILENT", # Default installation path is C:/Program Files (x86)/FORScan/ ], }
Creating a artificial .app folder to actually launch FORScan
Currently Homebrew can download and run the Windows Installer but we don’t yet have a nice
FORScan.app
in our Applications folder.We will need to create a minimal .app structure to actually launch the FORScan. It will not be self-contained and portable to other systems but we don’t care about that. We will need to create a executable into
FORScan.app/Contents/MacOS/FORScan
and a project metadata file into FORScan.app/Contents/Resources/Info.plist
. The end result will look like this:$ tree FORScan.app/ FORScan.app/ └── Contents ├── MacOS │ └── FORScan └── Resources └── Info.plist
We will abuse next Homebrew keyword
preflight
to create that but before that add a name for the .app
folder which Homebrew will look for into your formula:app "FORScan.app"
This will also tell Homebrew to move this folder to your Applications. Next let’s create it in the
preflight
phase of the cask.# This very tiny bash script will be our installer forscan_launcher_content = <<~EOS #!/usr/bin/env bash # Open the FORScan application itself through Wine open -a 'Wine Stable' "$HOME/.wine/drive_c/Program Files (x86)/FORScan/FORScan.exe" EOS # We need to add a PLIST file to tell MacOS about our application. # I'm not sure if this was actually needed in the end # Source: https://www.artembutusov.com/how-to-wrap-wine-applications-into-macos-x-application-bundle/ forscan_plist_content = <<~EOS <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleExecutable</key> <string>FORScan</string> <key>CFBundleGetInfoString</key> <string>FORScan</string> <key>CFBundleIconFile</key> <string>AppIcon</string> <key>CFBundleIconName</key> <string>AppIcon</string> <key>CFBundleName</key> <string>FORScan</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleSignature</key> <string>4242</string> <key>NSHighResolutionCapable</key> <true/> </dict> </plist> EOS # Create the Forscan.app folder structure and necessary files preflight do FileUtils.mkdir_p "#{staged_path}/FORScan.app/Contents/MacOS" File.write "#{staged_path}/FORScan.app/Contents/MacOS/FORScan", forscan_launcher_content FileUtils.chmod 0755, "#{staged_path}/FORScan.app/Contents/MacOS/FORScan" FileUtils.mkdir_p "#{staged_path}/FORScan.app/Contents/Resources/image.iconset" File.write "#{staged_path}/FORScan.app/Contents/Resources/Info.plist", forscan_plist_content end
Then you are done and should be able to use your Windows software through wine.
If you want your users to be able to cleanup the installation you could add
uninstall
keyword as well into your Cask formula:uninstall delete: [ "~/.wine/drive_c/Program Files (x86)/FORScan", "~/.wine/drive_c/ProgramData/Microsoft/Windows/Start Menu/Programs/FORScan", ]
After these changes the complete file should look like something like this:
cask "forscan" do version "2.3.62" sha256 "fad11dd5f91a86961b2435798eeae8241dd3a69c92808f5b9eedfe35eac2a77c" url "https://forscan.org/download/FORScanSetup#{version}.release.exe" name "FORScan" desc "Software scanner for Ford, Mazda, Lincoln and Mercury vehicles" homepage "https://forscan.org/home.html" livecheck do url "https://FORScan.org/changes_history.html" regex(%r{href=.*download/FORScanSetup(\d+(?:\.\d+)+)\.release\.exe}) end # Since FORScan is made for Windows we need to install Wine to run it depends_on cask: "wine-stable" # Virtual COM Drivers which work for vLinker FS USB OBD2 reader # They also should work for the OBDLink EX which is the other recommended cable # These are needed for the MacOS host machine to detect the OBD2 reader depends_on cask: "ftdi-vcp-driver" app "FORScan.app" # This very tiny bash script will be our installer forscan_launcher_content = <<~EOS #!/usr/bin/env bash # Open the FORScan application itself through Wine open -a 'Wine Stable' "$HOME/.wine/drive_c/Program Files (x86)/FORScan/FORScan.exe" EOS # We need to add a PLIST file to tell MacOS about our application. # I'm not sure if this was actually needed in the end # Source: https://www.artembutusov.com/how-to-wrap-wine-applications-into-macos-x-application-bundle/ forscan_plist_content = <<~EOS <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleExecutable</key> <string>FORScan</string> <key>CFBundleGetInfoString</key> <string>FORScan</string> <key>CFBundleIconFile</key> <string>AppIcon</string> <key>CFBundleIconName</key> <string>AppIcon</string> <key>CFBundleName</key> <string>FORScan</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleSignature</key> <string>4242</string> <key>NSHighResolutionCapable</key> <true/> </dict> </plist> EOS # Create the Forscan.app folder structure and necessary files preflight do FileUtils.mkdir_p "#{staged_path}/FORScan.app/Contents/MacOS" File.write "#{staged_path}/FORScan.app/Contents/MacOS/FORScan", forscan_launcher_content FileUtils.chmod 0755, "#{staged_path}/FORScan.app/Contents/MacOS/FORScan" FileUtils.mkdir_p "#{staged_path}/FORScan.app/Contents/Resources/image.iconset" File.write "#{staged_path}/FORScan.app/Contents/Resources/Info.plist", forscan_plist_content end uninstall delete: [ "~/.wine/drive_c/Program Files (x86)/FORScan", "~/.wine/drive_c/ProgramData/Microsoft/Windows/Start Menu/Programs/FORScan", ] end
Then just commit your changes and push them to your public Github repository:
cd /opt/homebrew/Library/Taps/$YOUR_GITHUB_USERNAME/homebrew-tap git add . git commit -am "Created FORScan Cask installer to use Windows software on MacOS" git push origin HEAD
After this your Windows software can be installed by running:
brew install --no-quarantine --cask wine-stable $YOUR_GITHUB_USERNAME/tap/forscan
Because the installer of our Cask formula depends on the wine being available in the system we need to give that as the first package to be installed before our custom tap can be used. I hope this was useful for you and will help you to run Windows software on your Mac with ease.
Extras: Automating complicated setup and dependencies
Because FORScan needs to use the USB serial device attached to the host machine these need to be symlinked to virtual Wine COM devices I needed to add a lot of extra ducktape and glue to make that experience easy for the users of my tap. I replaced the bash based launcher with Applescript which takes care that the necessary system extensions will be installed and the USB devices are connected and symlinked properly before actually opening the FORScan. There’s also some Windows regedit changes needed before Wine can see the USB devices.
If you’re curious you can check the complete Cask formulae I created in my own tap: https://github.com/onnimonni/homebrew-tap/blob/main/Casks/forscan.rb
It was a pretty fun experiment of how one can open programs and click their buttons automatically for the user. Now I don’t need to remember how to make this complicated setup manually anymore. It was a pretty complicated and time consuming journey but hopefully this will allow others to achieve same results easily in the future 🤝.