We're distributing software built on Swift 5.4 to a few million users and have been getting reports of missing DLLs on some systems. We've been using dumpbin to determine what DLLs to include in our installer, and things seem to work for the vast majority, but we still get reports sometimes.
Running dumpbin against our executable and the DLLs found in C:\Library\Swift-development\bin yields the following result:
It would be interesting to know what library is indicated as missing. The current dependency set would be VS2019 x64 Runtime (Latest supported Visual C++ Redistributable downloads | Microsoft Learn) which is also part of Visual Studio for embedding in an installer. Additionally, you should be distributing the ICU and Swift runtime MSIs (which currently, sadly require you to crack open the installer - its entirely a distribution setup thing that needs to be pushed on, the intent was always to have the MSI available separately as well for re-distribution). Everything else should be a system provided library.
Thanks @compnerd! We'll try that route out in our next release.
It would be very nice to have a "blessed" MSI from swift.org for redistribution of the ICU and Swift runtime for non-developers (like the JDK vs. JRE in Java land). That would allow an end-user to install manually or have it included in a product installer.
That is actually exactly the model that everything is setup for. The toolchain installer is setup as just an envelope for the MSIs, it installs 5 MSIs:
ICU (runtime component)
Swift Runtime (runtime component)
toolchain (core toolchain; e.g. clang, swiftc, lldb)
x64 SDK (developer SDK - this is meant to be configurable with i686, x64, ARM, ARM64 being available)
developer tools (spm, etc)
This also helps with the distribution sizes. The ICU MSI is large (previously brought up at ICU usage in Swift) and weighs in at 13 MB (compared to the SDK which is 10 MB). The runtime isn't exactly tiny either at 5 MB. The runtime components basically sum up to ~20 MB. Hopefully we will get to the point where we can start focusing on the distribution sizes to help reduce some of the cost.
My personal opinion is that products should really just bundle the runtime components, and because the runtime components would have the same product id, multiple packages installing it would not be a problem. Of course, there are a few more issues left to iron out still (namely versioning for parallel installation a la SxS). Fortunately, most of the work is just something which requires engineering effort, the solutions to them are pretty clear and simple to implement.
CC: @mishal_shah (for help with figuring out how to host the additional copies of the MSIs for redistribution sorted out)
Note that besides ICU (as installed by the toolchain installer into icu-67\usr\bin) and the Swift-DLLs (I think as installed into Swift-development\bin) you should only need to include the DLLs from [VisualStudioFolder]\VC\redist\...\x64\*.CRT in a distribution (note that you may not redistribute debug_nonredist, onecore\debug_nonredist), for legal issues of redistribution for those files from Visual Studio see the permission formulated in the official documentation by Microsoft. In addition, I include the following license files:
I tested it on a fresh Windows instance without any installations (only adding the paths of the according distributed folders to the PATH environment variable for the program).
And yes, ICU is big, the respective sizes being (for Swift 5.5):
ICU: 31,7 MB
the Swift DLLs: 14,6 MB
the files from the Visual Studio installation: 1,6 MB
I distribute those parts (together with the license information) in different folders that have names according to their versions and the "trick" for me is that those folders are "pre-installed" where my program is used and most times when those folders need to be updated only a new Swift DLL folder is needed.
No trick, very easy and straightforward. Following my description above, I have three directories with content as below. The paths to all three directories have to be added to your current PATH environment variable (this is when you want to distribute your program, be careful not to have these additions in your PATH during development). You can use this setting to run a Swift program compiled with the according Swift version without having to install anything (besides having those three directories) on a system.
@sspringer Say I know nothing about Windows and I'm shipping a zipped folder with the executable and the dlls it depends on (like you mentioned) to an end user. How do I add them to the current PATH environment without making the end user do anything?
@gregcotten then the best option would be to extract the runtime MSI and distribute that along with your program. The desire to distribute multiple artifacts for a release has been brought up multiple times, but needs work that only @mishal_shah can take care of.
In the mean time, just use dark to extract the runtime MSI from the installer. Something along the lines of:
dark installer.exe -x out
should do the trick assuming that you have the WiX tools on your path.
If you are shipping a zipped folder, then I argue that you should be expected to not only know about Windows but be expected to know more detailed system internals of Windows, such as SxS, manifest loading, etc. Redistribution of standalone applications on Windows is not the well-trodden ground, but rather the special advanced use-case. The well-trodden ground is distribution via installers. In such a case, adding in the additional MSI is not particularly complicated and neatly solves the issue.
@compnerd I'm working on a React Electron app distributed with Squirrel.Windows that calls into a Swift library using the Node-API, so it won't just be a bare executable. I'll probably just package the .dlls in the same folder as the result .node library and call it a day.
Entirely depends on you. It is possible to package it, but they may already also have it installed. So you would need additional infrastructure to install it as needed. Alternatively, you could require the user install it from Microsoft.
This is exactly what we've been doing since I originally posted last year. We collect the needed Swift runtime and ICU DLLs, and pair them with the DLL of the Swift library we are building that depends on them, then hand that to several teams to use as shared code.
One team produces an Electron app, loading our shared DLL to use in computations so all teams are relying on the same code. The "directories with content" you mentioned is the same list we use. We also make sure the user gets vcruntime140.dll as part of the install, completing the list @sspringer noted: Swift Runtime, ICU, Visual Studio installation.
Part of our test suite tests against a "naked" Windows install too, which has caught problems on numerous occasions and is definitely recommended.
Having distributed this way for a year to a large user base, we have confidence at this point that its sufficient to distribute this way. That being said, as @compnerd suggests, it might be better to simply crack open the installer from swift.org and add the MSIs to the installer that you use for your React Electron app. Makes dealing with the Visual Studio redistributable easier as well.
I think this should be documented on swift.org, which needs a “distribution of Swift programs” page. Both methods (installer and “portable”) seem to be valid use cases and should be included. Maybe one of us should start write an according README on Github, and after review we should present its content to the Website Workgroup.
For the installer part, there are the comments by @compnerd about integrating the contents of the Swift toolchain installer and about what to consider regarding the VS C++ distributables via installers. Maybe those hints suffice for a first version (I do not know enough about building installers).
For the portable part, it seems my comment from Nov. 21 seems to be the most detailed, but it should also be noted that you could put everything into one directory together with your program or library. (BTW, I forgot the license text for the Swift DLLs in my example listing.)
Additional note: Yes, in the long run static linking is one more method and maybe the most preferred one, but static linking on Windows is not there yet (and not the most important Windows issue at the moment, I think).
Absolutely agree, this would be a good idea. What do you think @compnerd? Would it be well received by the Website Workgroup to add a formal "End-User Distribution" section to the same side-menu as "Getting Started", "Download", and "Platform Support" with instructions for building installers from MSIs on Windows and building APT and RPM packages that depend on Swift packages for Linux?
No, this does not work. The DLLs need to be renamed, but this is a problem for Linux as well. Unfortunately, this is not currently possible due to the build setup in CMake which does not use the normal CMake rules which would allow us to rename the DLLs and .so files to use the version number.