It happens seldomly, but a couple of weeks ago I had the chance to look at some parts of the Windows Runtime port again and now I have some time to write about the outcome.
One of the tedious items when working on the port is that all applications have to be “pre-packaged” to be run on the host system. As this is not really a cross-compile environment, where you need to deploy an application and its dependencies to a device, it feels weird. On the other hand it reflects the real world scenario pretty well, which helps you as an app developer so that you do not forget to put items into the final package.
So right now the test build needs to look like this:
- Platform/Imageformat plugins…
That implies that you need to copy Qt every time you want to test something. This usually is not a problem for an application developer, who copies Qt once. Doing that while developing on Qt itself is time consuming and error-prone, as for each test application you need to copy again.
Browsing through the documentation I found the following option to be interesting: “Framework ”
There is not much documentation about it, but what you basically can do is to create a “Framework bundle package” and install that. Applications can then specify in their manifest that they depend on it like documented here.
There is no real information available how we can deploy that to a store, but what it basically would provide is something similar to Ministro on Android, meaning a Qt delivery package, which only needs to be downloaded once. You can check on your system, which frameworks are installed by calling Get-AppXPackage on the Windows Power Shell. For each package there is a section called IsFramework. Parsing through that you will figure out that only the C-runtime is deployed currently as a framework, which proves that there is no documentation for good reason.
First let me describe how you can create a framework and how you depend on it before I will talk about the problems and why we cannot use it.
To prepare packaging you will need a certificate to sign your framework, you create it like that (in an Admin shell!!):
MakeCert /n "CN=QtProject, O=QtProject, L=Berlin, S=Berlin, C=Germany" /r /h 0 /eku "126.96.36.199.188.8.131.52.3,184.108.40.206.4.1.3220.127.116.11" /e 06/06/2013 /sv qtkey.pvk qtkey.cer
Write down the company specification, you will need it again at a later step.
Now let’s create the pfx file, again you need to be admin:
Pvk2Pfx /pvk qtkey.pvk /pi <password> /spc qtkey.cer /pfx qtkey.pfx /po <password>
<password> specifies the password you gave to the certificate in the first step.
Following we will create the framework bundle and install it to the system:
- Move all files you need to a directory where you do the packaging
- Create your ApplicationManifest.xml file and remember to specify the same company details as in the certificate above. Otherwise signing will fail!
- Create an framework package:
makeappx pack /d package /h SHA256 /p qt5.appx
- Sign the package:
SignTool sign /a /v /fd SHA256 /f qtkey.pfx /p <password> qt5.appx
- Add yourself to the trusted area, otherwise Windows does not allow to side load the framework:
Certutil -addStore TrustedPeople qtkey.cer
- Finally install the package to the system:
So, we have Qt5 installed as a framework, now what? To make use of that in the application, your application needs to specify Qt as a dependency in the manifest. Please note that you have to specify the minimum version correctly, you cannot just depend on Qt5 in general.
In case you want to update the Qt5 package you must update the Qt Version number in the manifest. Windows does not allow to just exchange the package! Afterwards call makeappx/SignTool/Add-Appxpackage as described above. As you specified the minimum version in your application, it will automatically use that. However, you will need to update the version each time you try to update the registered Qt version.
Secondly, in case you remove the application, Qt will be deleted as well. As soon as no application depends on a framework, it is gone. And installing an application depending on Qt5 does not work, as there is no source information where to retrieve it from. Luckily the install steps are the same for first install and update install. Hence you will not notice this fact most of the time.
Also note that compared to application packages you cannot use “-Register”. This options allows application developers to use a directory as a virtual package. It does not enforce you to bundle the application etc. But as stated, for Frameworks this does not apply. You have to fully package, sign and install Qt, there is no development version or such. However, debugging into the framework works.
So there are already a lot of steps required to use Qt as a framework, but still we achieved the scenario we wanted to have. Exchanging Qt is (relatively) simple and we could run the test apps against that without requiring to reinstall them all the time. Let us now go into further downsides of the frameworks:
What I have been curious about is how you parse the content of a framework. To summarize: you don’t! You can only take what is inside the root directory and then you can also have libraries there only. Those libraries are then added to the resolving order for dependencies.
So, if your app bundle depends on the Qt5 framework, when you launch your app the system will figure it requires for instance Qt5Core.dll. After not finding it in the app bundle, it checks the Qt5 framework, locates it in the root folder and resolves it. For libraries everything is fine. But what about plugins? You cannot specify to search inside a “platforms” directory inside the Qt5 framework. Everything outside the root directory of a framework is just wasted space.
Exaggerating the approach with frameworks, you could consider frameworks for each plugin. Then your app manifest could specify the plugins to use: “jpeg image handler, sql database, winrt platform plugin”. It could actually simplify deployment as there is nothing you can miss during development, but it would cause troubles for startup times etc. Remember that all dependencies must be in the root directory. So if the plugins are scanned it would need to scan through all dlls in all frameworks, including Qt libraries, different plugins and fitting plugins.
So you could still bundle the plugins inside the application package and have the Qt libraries inside the framework. This might work as long as we assume there is no behavior difference between different versions, nor compatibility breaks. Also, it does not help in our initial problem, meaning to speed up Qt development itself. So we are at a dead-end here.
The result of this far too long entry? Frameworks seem to be an interesting approach, but they do not fulfill our needs for development on Qt, neither for application developers using Qt. At least right now, documentation is lacking, public information is basically non-existent. Certainly the concept is a good starting point, but there is quite some steps missing. In addition to this, there have been some efforts towards implementing a command line tool, which helps you as a developer to get all files and dependencies on Windows (and Windows RT) in place, namely windeployqt. For those of you interested it works fairly well inside the port and also for Qt5 based desktop applications already and we will put some further efforts into finalizing this one.