(EDIT: This is out of date as Holochain is native on windows now. I’ll do an update in a reply)
Overview
As some of you may know, I have been working on bringing the SnapMail app to life on Holochain, on behalf of Harris-Braun Enterprises.
I have succeeded in creating releases of the SnapMail happ, which are downloadable as pre-packaged Mac, Linux and Windows desktop applications, by following Connor’s article on How To Ship Your hApp. But his article does not cover Windows, so here I am to explain the extra work that is required to have Windows support.
A Windows package will only work on Windows machines which have Windows Subsystem for Linux 2 (WSL2) installed as Holochain doesn’t work natively on Windows.
WSL2 has been rolled out in the May 2020 update, which is Windows 10 version 2004.
According to this news, Microsoft backported WSL2 to prior versions Windows 10 which brings WSL2 to the Windows 10 May 2019 and November 2019 updates. This can be helpful for setups who can’t upgrade to the latest Windows.
So for your hApp to work on Windows, the users must first make sure they have a WSL2 installed and a working Ubuntu distro.
Code changes
(For those who prefer looking at code to understand, I’d suggest loading the snapmail-release repo in your favorite IDE and look for all occurances of win32
and wsl
).
Basically the trick is to add hc
& holochain
executables that are built for Ubuntu to your bundle in a known subfolder, and to add the path to that folder to Electron’s process.env.PATH
. That way, you don’t need to build and setup holochain within Ubuntu. Have your happ spawn holochain within WSL2 with the updated process.env
. That way holochain
is added to Ubuntu’s PATHS and can be called from any location like a normal install! Magic!
The code for setting PATH is:
/** Add Holochain bins to PATH for WSL */
const BIN_DIR = "bin";
const BIN_PATH = path.join(__dirname, BIN_DIR);
if (process.platform === "win32") {
process.env.PATH += ';' + BIN_PATH;
}
In your code, when spawning holochain as a subprocess, you need to check if you are running on Windows and change the arguments of the spawn call so it calls wsl
instead of hc
and holochain
directly:
In other words, instead of calling:
holochain -c config.toml
you have to call:
cmd.exe /c wsl holochain -c config.toml
Here is the code I used:
// spawn "holochain"
let bin = HOLOCHAIN_BIN;
let args = ['-c', wslPath(CONDUCTOR_CONFIG_PATH)];
if (process.platform === "win32") {
bin = process.env.comspec;
args.unshift("/c", "wsl", HOLOCHAIN_BIN);
}
// spawn subprocess
g_holochain_proc = spawn(bin, args, { ... })
Notice the call to wslPath()
. This is because we need to change the paths we use from a windows point of view to a WSL/linux one. Because once calling wsl
you are in that space and must give paths compatible for that system, i.e change C:\Users\alex
to /mnt/c/Users/alex
.
Luckily for us, WSL provides a path converter that you can call by doing wsl wslpath -a <path>
.
This is what my wslPath()
function does. And will make sure to not change the path if you are not on Windows.
Also, remember that the conductor config is generated at first launch of the electron happ, so make sure to use wslPath()
when generating the conductor config since it will be used by holochain within WSL2. Example:
// replace dna
newConductorConfig = newConductorConfig.replace(
/file = 'dna'/g,
`file = "${wslPath(newDnaFilePath)}"`
);
Finally, I encountered some problems reading the DNA hashes that are stored in their own file. Since the line endings are different on Windows, a line ending character was being inserted in the DNA hash when reading it from disk. The workaround I found was to add a regex match when reading the file:
let snapmailDnaHash = fs.readFileSync(path.join(__dirname, SNAPMAIL_DNA_HASH_FILE)).toString();
// Make sure there is no trailing end of line
let regex = /(Qm[a-zA-Z0-9]*)/;
let match = regex.exec(snapmailDnaHash);
snapmailDnaHash = match[1];
We should now be all set to have a working Windows build!
For added robustness I have added a killAllWsl()
function, that will kill any pending holochain
process running within WSL2. This function is called at start and shutdown of the electron happ.
I recommended this because if a crash occurs in your happ, it will properly kill the WSL call but not the spawned holochain
process running within WSL2.
So to avoid previous crashes from crippling the user’s system we have to clean things up manually. This is done by calling wsl killall holochain
.
Be careful though that this will also kill any holochain process that has been started by any other happ. It would be best to uniquely name the holochain executable that you package with your happ.
Building & packaging
To package a windows bundle, add this script to your package.json
:
"package-windows": "electron-packager . Snapmail --platform=win32 --out=build --overwrite",
Make sure to add the hc
and holochain
linux binaries at the correct location before running the script.
As I work only on Windows, I have not tested if cross-compilation of the windows bundle works on other systems. On my side, I did manage to build a working Linux package from Windows and did not have a working one for Mac. I had to build the Mac package from macOS to have it working.
I do not have CI nor nix setup for Windows yet, and will probably not in a near future as for now the focus will be to switch to Holochain RSM!
Anyhow, I hope you found this useful. I will probably add more to it if I figure out better ways to do it and if people have questions or needs help troubleshooting issues.