Tips for streamlining KDE applications for deployment on Microsoft Windows
In KDE we have a great group of developers hacking on a variety of applications. We usually have no problems getting our software out to end users on Linux because distributions help us packaging and distributing our software.
Where we do have problems, for the major applications, is ensuring our software works on other platforms such as Windows and macOS. This short guide gives a few tips to understand how to make your KDE pet project work on Windows by at least testing it once on said system and verifying a few basic things.
Build your application on Windows
Set up a Windows VM (Windows 7+), a compiler (MSVC2015+ (recommended) -- or let Craft auto-setup MinGW during bootstrap) and build your application using Craft. If set up properly, all you need to do now is to run something along:
craft filelight
Craft will take care of installing all dependencies required to build filelight (i.e. deps of Qt5, Qt5, deps of KF5, KF5, ...) and only then builds the filelight project.
The end result is one single install root which contains the images of every package you've just built. Now starting filelight is as easy as running this in the terminal:
filelight
Run the unit tests
Again, easy to do with Craft. If your pet project has some unit tests availabe, you can easily run them by invoking e.g.:
craft --test kcoreaddons
This will invoke ctest
in the correct build directory and run the project's test suite.
Package the application
Craft on Windows has functionality to create installers out of installed packages.
craft --package filelight
The way this is implemented is pretty simple but powerful
- Craft collects all files of every image directories of the packages your application depends on
- Craft collects all files of the application's image directory
- Craft puts them into an intermediate install root.
- After that, Craft will strip unneeded files according to blacklists (cf. 'blacklist.txt' and similar functionality in blueprints)
- After that custom scripts may be run
- After that
makensis
is called (from the NSIS installer framework) which basically zips up the whole install root and generates a final installer executable
Things you usually need to fixup
Installer icon
Handled by: Craft
The very first impression counts, so why don't make your installer binary as sexy as possible?
Again let's take the installer generated for filelight. One version without an installer icon set, and one with the filelogo set as logo:
This is very easy to do with Craft which contains a few helpers to instruct the NSIS installer framework (the scriptable tool we use to generate Windows installers to begin with) properly to our likings.
An exemplary patch in craft-blueprints-kde.git (KDE's blueprint collection for Craft):
commit 1258a4450a1ee2f620856c150678dcaf5b5e7bad
Author: Kevin Funk <kfunk@kde.org>
Date: Mon Nov 20 14:13:04 2017 +0100
filelight: Add application icon
Created with:
convert /usr/share/icons/breeze/apps/48/filelight.svg ./kde/kdeutils/filelight/filelight.ico
diff --git a/kde/kdeutils/filelight/filelight.ico b/kde/kdeutils/filelight/filelight.ico
new file mode 100644
index 0000000..1b6a71b
Binary files /dev/null and b/kde/kdeutils/filelight/filelight.ico differ
diff --git a/kde/kdeutils/filelight/filelight.py b/kde/kdeutils/filelight/filelight.py
index 0003f30..ac602c6 100644
--- a/kde/kdeutils/filelight/filelight.py
+++ b/kde/kdeutils/filelight/filelight.py
@@ -30,6 +30,7 @@ class Package(CMakePackageBase):
self.defines["productname"] = "Filelight"
self.defines["website"] = "https://utils.kde.org/projects/filelight/"
self.defines["executable"] = "bin\\filelight.exe"
+ self.defines["icon"] = os.path.join(self.packageDir(), "filelight.ico")
self.ignoredPackages.append("binary/mysql")
self.ignoredPackages.append("libs/qt5/qtdeclarative") # pulled in by solid
This patch adds an ICO file to the repository and references it in the filelight blueprint. Craft takes care of telling NSIS to use this ICO file as the installer icon internally while building your package with craft --package filelight
.
Application icon
Handled by: CMake
Imagine starting your application via the Windows Start Menu:
For getting the custom application, you need to embrace using Extra CMake Modules ECMAddAppIcon module which provides the CMake function ecm_add_app_icon(...)
which in turn allows you to amend your executable with an application icon.
Here's an exemplary patch taken from filelight.git:
commit d7c7f1321547197e5bb9ceba6b8ccc51790bef8b
Author: Kevin Funk <kfunk@kde.org>
Date: Mon Nov 20 23:14:30 2017 +0100
Add app icon
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4c1e5dc..b8b6eda 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,6 +25,7 @@ cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR)
find_package(ECM 1.3.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
+include(ECMAddAppIcon)
include(ECMGenerateHeaders)
include(ECMInstallIcons)
include(ECMMarkNonGuiExecutable)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 622c9d8..06f5e8d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -34,8 +34,16 @@ set(filelight_SRCS
summaryWidget.cpp
historyAction.cpp
mainWindow.cpp
- main.cpp)
-
+ main.cpp
+)
+set(filelight_ICONS
+ ${CMAKE_CURRENT_SOURCE_DIR}/../misc/16-apps-filelight.png
+ ${CMAKE_CURRENT_SOURCE_DIR}/../misc/32-apps-filelight.png
+ ${CMAKE_CURRENT_SOURCE_DIR}/../misc/48-apps-filelight.png
+ ${CMAKE_CURRENT_SOURCE_DIR}/../misc/64-apps-filelight.png
+)
+ecm_add_app_icon(filelight_SRCS ICONS
+ ${filelight_ICONS})
ki18n_wrap_ui(filelight_SRCS dialog.ui)
add_executable(filelight ${filelight_SRCS})
Use Breeze icon theme
Handled by: Craft -- you don't need to do anything.
Breeze-icons (KDE's default icon theme), when configured with te CMake option -DBINARY_ICONS_RESOURCE=ON
, installs .rcc files (binary resources, loadable by Qt).
Craft by default passes -DBINARY_ICONS_RESOURCE=ON
when the breeze-icons
package is installed and thus the RCC file is available by default. When a Craft blueprint using breeze-icons
is packaged, the RCC file is automatically included in the resulting artifact.
When the application starts up, KIconTheme scans some directories for RCC files, and if it finds them they're automatically opened and loaded => icons are available.
Further reading about the initial design of the feature by David Faure: https://blogs.kde.org/2016/06/16/icon-theme-deployment-windows-mac-os-and-mobile-platforms
Check presentation of file paths in user interface
Make sure that file paths in your application are rendered consistently. One usual problem we face is that the user interface ends up with strings like C:/Program Files (x86)/KDevelop\
, which makes use of forward and backward slashes inconsistently.
Instead, decide for one form of slashes. While on Windows backward slashes are the usual form; the Qt framework makes it a little difficult to print path names with them. API such as QUrl::toDisplayString(...)
will always return paths using forward slashes, and one would need to add little helpers everywhere using QDir::toNativeSeparators
to do it properly.
At the very minimum use either form; don't mix forward and backward slashes in one file path. Applications on Windows these days handle paths containing forward slashes just fine, by the way.
commit 0de04d386e403ded74554951a8c4dcb9ee9bc1f9
Author: Kevin Funk <kfunk@kde.org>
Date: Mon Nov 20 15:32:54 2017 +0100
File::fullPath: Nicer file path on Windows
diff --git a/src/fileTree.cpp b/src/fileTree.cpp
index 9a5d06e..28ed689 100644
--- a/src/fileTree.cpp
+++ b/src/fileTree.cpp
@@ -21,6 +21,8 @@
#include "fileTree.h"
+#include <QUrl>
+
QString
File::fullPath(const Folder *root /*= 0*/) const
{
@@ -32,5 +34,6 @@ File::fullPath(const Folder *root /*= 0*/) const
for (const Folder *d = (Folder*)this; d != root && d; d = d->parent())
path.prepend(d->name());
- return path;
+ const QUrl url = QUrl::fromLocalFile(path);
+ return url.toDisplayString(QUrl::PreferLocalFile | QUrl::StripTrailingSlash);
}
Install C/C++ runtime
Handled by: Craft -- you don't need to do anything.
When installing an application on Windows, the package author also needs to make sure the appropriate C/C++ runtime is injected into the system as part of the installation process. For instance, if your project was compiled using Microsoft Visual C++ 2015 and you want this project to run on another machine, you need to make sure the Microsoft Visual C++ 2015 Redistributable (which contains the C/C++ runtime components) is installed there.
Packages we need on Windows:
- If project compiled with MSVC:
- VCRedist installer (contains all necessary libraries)
- If project compiled with MinGW:
- Needs another set of libraries (e.g. libstdc++-6.dll, libgcc_s_sjlj-1.dll, ...)
But, don't be desperate: Craft has that all covered and will automatically include the binaries for either C++ runtime in the package and make sure it is properly installed as part of the installation of your KDE application on the target machine.
More ideas
If you'd like to know anything else I can probably add a few more paragraphs to this blog post for future reference. Just comment / mail me!