Incomplete library installs never get cleaned up

In older versions of zx/vapor, timeouts could result in broken libs that only got half installed.

2025-04-24T13:36:49.669000+10:00 error: Error in process <0.351.0> with exit value:, {{badmatch,{error,bad_directory}},[{zx_lib,build,0,[{file,"src/zx_lib.erl"},{line,676}]},{zx_daemon,make,1,[{file,"src/zx_daemon.erl"},{line,1787}]}]}

Once a library is in this state, any application that depends on this library will silently crash, and on Windows it might even leak an Erlang process into the background. The issue doesn't resolve until you manually delete the broken lib.

This doesn't happen as often anymore, since zx (and vapor?) time out less aggressively now, but if users get impatient and kill installation themselves, then they could easily recreate this issue and silently brick whatever Vapor app they wanted to run. Then, the fact that these broken libs are possible means there is forever one more "oh could you check this?" that we have to deal with in QPQ when users say gajudesk won't open.

One band-aid solution to this specific error message would be to check if the ebin directory exists, and create it if it is missing, but every other file that is missing in src, ebin, priv, will still just cause silent failures that will confuse the end user, so it seems to me like the fundamental issue is one of synchronization, rather than hygiene.

My humble suggestion is that zx adds some kind of (reverse) lock/canary file while it is installing the lib, inside the lib itself, or in some secret zomp directory, and then deletes the canary once the install is complete. Then next time it goes to load a lib, if the canary is present, it brutally deletes the whole directory and starts over. That way as devs we can still mess with our local libs with total freedom, but end users won't have to worry about lib synchronization at all, everything is either correctly installed, or not installed at all.

Edited by Jarvis Carroll