-
Notifications
You must be signed in to change notification settings - Fork 904
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add windows gui / msi installer [difficult] #253
Comments
Depending on OpenGL for Conrod might not work very well if they don't have the GPU manufacturer drivers that provide OpenGL, or if they're using Windows in a VM. |
We could just use Inno Setup... It will allow us to produce a GUI installer which can call out to the command line rustup behind the scenes, and it's configured declaratively, so we won't even need any non-rust code. |
@Diggsey Inno Setup does not produce msi's (I believe), and msi's are the delivery format people most expect and prefer. Rust itself uses Inno to produce .exe installers but we don't advertise them because the .msi's are nicer. |
If we use the Windows Installer stuff then that can provide a user interface for us. |
.msi is a really big pain due to windows installer cache bloat :( |
If it's just a window with some text and [Install]/[Cancel] buttons, the built-in msi UI will do just fine. Anything more complicated/dynamic than, say, the Rust installer, would be quite annoying to do in it, and in that case I would suggest going with WinForms/WPF. I also want to note that invoking an .exe to perform the actual system changes (like mucking with the registry to modify PATH) goes against the "best MSI practices", but I guess we'd be fine with it for cross-platform consistency's sake? |
@vadimcn Modifying the PATH would have to be done in a custom action anyway, even if .msi is used, so there's no real benefit in that regard. Aside from that, rustup doesn't actually make any changes to your system (even rustup itself goes in your user folder) |
Why? MSI has direct support for modifying environment vars. |
@vadimcn I don't want to invoke an exe. What I want to do is have the msi system call functions in the multirust dll to perform the installation actions, while presenting the UI people expect from an msi installer. Can we do that? |
Yes, that is possible. |
In any cases, please keep supporting non-Administrator users installing Rust to somewhere under |
You may also try using WiX (http://wixtoolset.org/), it's a declarative way of creating a Windows Installer (msi) that can be heavily customized. It also says that it supports custom actions written in C++, so it shouldn't be too hard to use custom actions written in Rust. On the other hand, however, this might be too big of a non-Rust build system/dependency. |
As a user, I've always preferred Inno Setup or NSIS installers over msi. Most msi installers I've used are slower and don't have as nice of a UI as the open source installers. They've also been buggier, but that might not be the Windows Installer's fault. I really don't want a UI made with conrad or .NET. Conrad does not look native and either one would add overhead. I've written an NSIS installer before with similar screens, and I know it can call C functions. I'd be willing to work on an NSIS installer if there was a chance it would be used. |
How about this https://github.com/andlabs/libui |
How about using Qt Quick ? It is more mature than libui, has better documentation, the problem of high dpi (4K UHD) screens is solved too. |
@tiborgats that would add an unnecessary dependency for QtQuickControls etc for a simple installer. |
I'd like to keep using WiX for this. Seems to be the most 'modern' choice. It's what we're using today. |
The way to get started here is just prototyping: figure out how to make WiX, the rustup library and the Win32 GUI APIs work together to present something that looks plausibly like an installer. |
I tried to get this working and have a prototype running that ...
The only issues I encountered were the following:
I have uploaded my experiments here: https://gist.github.com/Boddlnagg/9d8f01e6d844cd78473651470282ebda I might be able to work more on this over the next weeks, but I don't want to keep anyone else from doing so, when they are faster. |
Further investigations led me to the MsiProcessMessage function (though it is better to use the The rustup installation routines will have to be refactored in such a way that status updates can be either reported to the console or via |
@Boddlnagg thanks for doing that research! Using a registry key for registration seems just fine.
This seems ok. We can use a 32-bit installer everywhere.
Makes sense. In order to get the self-install to work from a library we're going to have to do some refactoring of the project structure. Right now there are two top-level artifacts: the
With those refactorings we can whip up a basic installer that presents no options but does put the stuff in the right place. |
(I can probably help with the |
To avoid duplication it's technically possible to load an executable as though it were a DLL and call functions from it - http://www.codeproject.com/Articles/1045674/Load-EXE-as-DLL-Mission-Possible there's probably a cleaner way though. |
I skimmed over https://github.com/rust-lang-nursery/rustup.rs/blob/master/src/rustup-cli/self_update.rs and was thinking that maybe we could remove the complex Windows-only logic to remove the running exe, by always using the MSI for uninstall/update (installed MSIs are cached, so it will exist on the system). That requires that rustup has been installed using the MSI, but if that will be the only method on Windows (i.e., rustup-init will no longer exist), it should be fine. I don't know about a possible upgrade path for already existing installations that have never used the MSI, though. Also some other routines, that have Windows-specific codepaths (such as updating the PATH) could be handled by MSI directly. WiX provides ways of doing this easily, since it's something that installers often do. |
@Boddlnagg Just tell the user to delete their rustup installation and download and install using the MSI instead? |
@retep998 That would be possible, of course, but will it be convenient enough? @brson I don't quite understand these sentences:
Why are you talking about the rustup binary? I was expecting that the MSI just includes the custom action DLL ( |
I think this is a reasonable goal to aim for, though perhaps we can get there incrementally. If it ends up just being a lot easier to implement then maybe we can jump straight to that model. The transition story can be worked out later - there's a lot of problems to solve just to get to the point where we have a working GUI installer.
It's not obvious to me that it should be extracted by the msi itself since there is other logic to installation than just extracting the binary, but if we can make it work that way then that's probably best. The logic for deciding the installation is in The bulk of the GUI customization work we'll need to do is for configuring the global installation options and for installing/updating/uninstalling toolchains. If the basic work of putting the rustup bin in the right place can be done by the MSI system itself that seems good. |
@Boddlnagg if you find yourself needing to make changes to rustup self update to support the new uninstall then can I suggest doing it under a feature flag for now so we can continue producing rustup's with the current model while we iterate on the new installer? |
@brson Sure, I will use a feature flag. But I won't be able to do much now until the end of next week, just as a heads-up. (Maybe someone else wants to chime in and already start with the refactorings? 😉 ) |
I'll put it on my todo list to start refactoring the installer code, but suspect I won't get to it this week. |
I just went through self_update.rs and tried to identify those parts that need to be shared by the MSI installer. To be able to reuse them, those parts will need to be moved from the executable into the library (and potentially refactored and adapted).
|
Thanks for doing the analysis! |
I opened an initial WIP PR at #635. |
Thanks @Boddlnagg. Great progress. |
We've merged the first iteration, which puts the basic structure into place. Thanks @Boddlnagg! It's even set up to build on appveyor, but there's a bit left to do. What I know of:
@Boddlnagg can you give a braindump of other remaining work? |
I had already prepared a table of steps that need to be executed in the final installer (this should probably also be placed somewhere in the code as documentation). I marked those that are already implemented (unfortunately checkboxes don't work within tables). Some steps also need a bit of discussion (e.g. uninstall previous MSI-installed Rust automatically? Is cleanup_legacy() still required?). The biggest chunk, where I could really use some help, is "install default toolchain, show status/progress in UI". This requires a refactoring of the status reporting during toolchain installation.
CA is short for "Custom Action". "RustupPrepare" is currently called "RustupSetInstallLocation" but should be renamed. |
Also, most of the |
Great progress, thank you ! |
I tried to improve the error handling inside the Custom Actions, but found that Custom Actions always should have a corresponding Rollback Custom Action that rolls back the changes done by the normal CA whenever one of the installation steps fails. There is an option to disable rollback completely, but this doesn't seem to work reliably (in my tests, a rollback was performed regardless). All the possible scenarios (successfull/failed installation/upgrade/uninstall) need to be tested thoroughly, so to do it right, everyone recommends to use Custom Actions as sparingly as possible, and instead rely on proven built-in actions. We won't be able to get rid of all CAs (e.g. to create hardlinks and do the toolchain install), but we should wherever we can, even though this means less shared code between platforms. I don't know how important it is that rollback works correctly, but MSI users usually expect it to work (on a failed installation the installer even explicitly says "Your system has not been modified"). |
@Boddlnagg It seems reasonable to me that as much of the |
@brson We could get rid of custom actions completely by creating copies of |
I would be okay with the rustup installer not actually installing any toolchains and just installing rustup itself. |
@Boddlnagg That sounds like a good starting point, though I think ultimately we do want to be in a place where the GUI can be used for all installation tasks - that's what windows users expect. Losing the hardlinks is kind of a drag though. Is it desirable to not have custom actions? |
@brson It is desirable to not have custom actions, because of rollback: When Windows Installer installs a file and would have to overwrite an existing file, it first creates a backup of that file at some hidden location, then installs the new file, then deletes the backup when the installation has completed. We would have to implement this logic ourselves for the "Create Hardlink" custom action in order to support rollback correctly, and I don't know if that's worth it, if we can use machinery that's already there. Do you expect that the number of hardlinks will grow in the future? A GUI for all installation tasks (i.e. for rustup itself, not just for the initial setup) should not be the goal for the MSI. MSI is just not built for downloading additional data and running arbitrary application logic, it is meant for installing files and changing system settings. Even WiX uses custom bootstraping applications for things that go beyond that. One could build on top of the Windows Installer infrastructure to install toolchains, but that would require to repackage every installable package as Windows Installer modules, and it would still require an additional GUI for managing everything (because there seems to be no way to dynamically update the features/modules that are available in an installer) and the Windows version would be basically totally different from all other platforms, which constitutes a heavy maintenance burden. One could write such a GUI, but not using WiX/MSI ... however, I think a graphical user interface for rustup should rather be part of an IDE. So users who don't want to use these graphical tools can still use the command line as on any other platform, and IDEs can provide toolchain management by wrapping |
NSIS Anyone? |
@Boddlnagg ok, thanks for the explanation. If it's not appropriate to have a component within the MSI then we shouldn't. As a starting point just getting a GUI replacement for rustup-init is great. Though rustup-init does allow component selection. Would you see us not having any kind of of screen in the MSI that allows for selecting components? I still feel like for the best experience there should be a GUI that allows one to do component selection at install / update time. Take the VS installer for example. That's the kind of experience I think windows users expect. I do understand the notion that IDES should handle rustup installation, and agree. I hope we get to the point in Rust where we can say "Install VS code, install the Rust plugin", and the plugin deals with rustup however is most appropriate for VS code. But there are a lot of IDEs, and they're not all going to reach that level of integration. |
It is definitely possible to have a screen to select components, but I think (at least I didn't find a way to do it – though I would not consider myself an expert with MSI) they need to be hardcoded into the MSI, so can't be updated on the fly when the installer is run. This is what I meant when I said that dynamic updates to features are not possible. This makes it unsuitable to e.g. have every single Rust nightly release as selectable MSI component. |
@brson What exactly is the definition of a "component" in this context? How dynamic is the set of selectable components? |
@rustbot label: +O-windows |
Rust is very developer-friendly with its tooling and most areas of the experience. But having to use a console-based installer is really not a great first impression. It also doesn't make the MSVC requirement very clear and it's easy to skip by accident. A GUI could help clarify that. This would bring Rust's installation process more on par with other languages and dev tooling I'm used to. I can't think of any other programming languages that require a command line installation process. |
To complete the Rust installation experience on Windows we want to be installing rustup via an msi.
Make a proof-of-concept rustup msi installer that embeds libmultirust. The behavior of this msi will be heavily customized - all it does is the standard rustup install, but presented in a windowsy way. To start with it can be really simple:
This will require a lot of refactoring of the existing install code to get it embedded in this new context.
Our options for GUI's will be limited but we can't use something heavy. I'm thinking either something rust-centric like conrod, or just a very thin win32 wrapper.
The text was updated successfully, but these errors were encountered: