MSI, including associations
Alpine 3.24 Success

This commit is contained in:
2026-08-24 22:47:35 +02:00
parent 91eef514d0
commit 219393cf2b
4 changed files with 187 additions and 6 deletions
+17
View File
@@ -270,3 +270,20 @@ set(CPACK_PACKAGE_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CPACK_PACKAGE_FILE_NAME
"${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
include(CPack)
# CPack's WIX generator needs the real WiX toolset (Windows or wine).
# We've already figured out msitools.
if (WIN32 AND CMAKE_CROSSCOMPILING)
set(DAWN_ICON_ICO "${PROJECT_BINARY_DIR}/dn/dn.ico")
set(dawn_wxs "${PROJECT_BINARY_DIR}/dawn.wxs")
configure_file(dawn.wxs.in "${dawn_wxs}" @ONLY)
set(dawn_msi
"${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_PROCESSOR}.msi")
add_custom_target(msi
COMMAND "${PROJECT_SOURCE_DIR}/msys2-package-msi.sh"
"${CMAKE_SYSTEM_PROCESSOR}" "${dawn_msi}" "${dawn_wxs}"
"${PROJECT_DESCRIPTION}"
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
VERBATIM)
endif()
+74
View File
@@ -0,0 +1,74 @@
<?xml version='1.0' encoding='utf-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<?define FullName = "@PROJECT_NAME@ @PROJECT_VERSION@" ?>
<?if $(sys.BUILDARCH) = x64 ?>
<?define ProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else?>
<?define ProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif?>
<Product Id='*'
Name='$(var.FullName)'
UpgradeCode='a99226f0-c9a4-4cc8-8065-bb79dc3c3469'
Language='1033'
Codepage='1252'
Version='@PROJECT_VERSION@'
Manufacturer='The dawn Authors'>
<Package Id='*'
Keywords='Installer,Image,Browser'
Description='$(var.FullName) Installer'
Manufacturer='The dawn Authors'
InstallerVersion='200'
Compressed='yes'
Languages='1033'
SummaryCodepage='1252' />
<Media Id='1' Cabinet='data.cab' EmbedCab='yes' />
<Icon Id='dn.ico' SourceFile='@DAWN_ICON_ICO@' />
<Property Id='ARPPRODUCTICON' Value='dn.ico' />
<Property Id='ARPURLINFOABOUT' Value='https://git.janouch.name/p/dawn' />
<UIRef Id='WixUI_Minimal' />
<!-- This isn't supported by msitools, but is necessary for WiX.
<WixVariable Id='WixUILicenseRtf' Value='License.rtf' />
-->
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='$(var.ProgramFilesFolder)'>
<Directory Id='INSTALLDIR' Name='$(var.FullName)' />
</Directory>
<Directory Id='ProgramMenuFolder'>
<Directory Id='ProgramMenuDir' Name='$(var.FullName)' />
</Directory>
<Directory Id='DesktopFolder' />
</Directory>
<DirectoryRef Id='ProgramMenuDir'>
<Component Id='ProgramMenuDir' Guid='*'>
<Shortcut Id='ProgramsMenuShortcut'
Name='@PROJECT_NAME@'
Target='[INSTALLDIR]\dn.exe'
WorkingDirectory='INSTALLDIR'
Arguments='"%USERPROFILE%"'
Icon='dn.ico' />
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
<RegistryValue Root='HKCU'
Key='Software\[Manufacturer]\[ProductName]'
Type='string'
Value=''
KeyPath='yes' />
</Component>
</DirectoryRef>
<!-- FileAssociations is defined in the generated associations.wxs:
it needs dn's actual "list supported media types" output. -->
<Feature Id='Complete' Level='1'>
<ComponentGroupRef Id='CG.dn' />
<ComponentRef Id='ProgramMenuDir' />
<ComponentRef Id='FileAssociations' />
</Feature>
</Product>
</Wix>
+23 -6
View File
@@ -9,6 +9,7 @@
#include "dawn-config.h"
#include "libdn.h"
#include "thumbnail-cache.hpp"
#include "xdg.hpp"
#if DN_WITH_SINGLE_INSTANCE
#include "instance.hpp"
@@ -26,6 +27,7 @@
#include <QGuiApplication>
#include <QUrl>
#include <qcommandlineoption.h>
#include <qcoreapplication.h>
#include <cstdio>
#include <cstring>
@@ -160,14 +162,20 @@ main(int argc, char **argv)
QStringLiteral("Output supported media types and exit."));
parser.addOption(list_supported_opt);
const QCommandLineOption list_extensions_opt(
QStringLiteral("list-supported-extensions"),
QStringLiteral("Output supported filename globs and exit."));
parser.addOption(list_extensions_opt);
parser.addPositionalArgument(QStringLiteral("path | URI"),
QStringLiteral(
"Image file or directory. Repeat to open multiple windows. "
"Defaults to the current directory."),
QStringLiteral("[path | URI]..."));
// I suppose this could very well be passed a QStringList; this is shorter.
parser.process(QCoreApplication(argc, argv));
// xdg_data_dirs() invokes the static QCoreApplication::instance().
auto application = make_unique<QCoreApplication>(argc, argv);
parser.process(*application);
if (parser.isSet(invalidate_opt)) {
dn::thumbnail_cache_invalidate();
@@ -178,12 +186,21 @@ main(int argc, char **argv)
printf("%s\n", type.c_str());
return 0;
}
if (parser.isSet(list_extensions_opt)) {
vector<QString> types;
for (const string &type : dn::supported_media_types())
types.push_back(QString::fromStdString(type));
for (const QString &glob : dn::extract_mime_globs(types))
printf("%s\n", glob.toUtf8().constData());
return 0;
}
QGuiApplication application(argc, argv);
application.reset();
application = make_unique<QGuiApplication>(argc, argv);
const QStringList raw = parser.positionalArguments();
// Without the working directory, relative arguments do not resolve to a
// local file, and every one of them silently opens the CWD instead.
// Without the working directory, relative arguments do not resolve to
// a local file, and every one of them silently opens the CWD instead.
const QString cwd = QDir::currentPath();
QStringList to_open;
@@ -241,5 +258,5 @@ main(int argc, char **argv)
if (app.open(path, {}, {}, browse) != dn::OpenResult::Ok)
return 1;
}
return application.exec();
return application->exec();
}
+73
View File
@@ -0,0 +1,73 @@
#!/bin/sh -e
# msys2-package-msi.sh: build an MSI from a MinGW/MSYS2 cross-build
export LC_ALL=C
arch=$1 msi=$2 wxs=$3 description=$4
destdir=$PWD/package/${msi%.*}
# CMAKE_SYSTEM_PROCESSOR is x86_64 for a 64-bit MinGW cross-build.
[ "$arch" = "x86" ] || arch=x64
# Wine 11 has removed wine64, so this is only backwards compatibility.
if command -v wine64 >/dev/null
then wine() { command wine64 "$@"; }
fi
rm -rf "$destdir"
cmake --install . --prefix "$destdir"
txt2rtf() {
LC_ALL=C.UTF-8 iconv -f utf-8 -t ascii//translit "$@" | awk 'BEGIN {
print "{\\rtf1\\ansi\\ansicpg1252\\deff0{\\fonttbl{\\f0 Tahoma;}}"
print "\\f0\\fs24{\\pard\\sa240"
} {
gsub(/\\/, "\\\\"); gsub(/[{]/, "\\{"); gsub(/[}]/, "\\}")
if (!$0) { print "\\par}{\\pard\\sa240"; prefix = "" }
else { print prefix $0; prefix = " " }
} END {
print "\\par}}"
}'
}
# msitools have this filename hardcoded in UI files, and it's required.
txt2rtf "$(dirname "$0")/LICENSE" > License.rtf
find "$destdir" -type f \
| wixl-heat --prefix "$destdir/" --directory-ref INSTALLDIR \
--component-group CG.dn --var var.SourceDir > package-files.wxs
# Only register extensions in Explorer's "Open with" list, never claim
# HKCR\.ext itself: Windows 8+ silently ignores default-app changes anyway,
# and doing it by hand here would just hijack the user's existing default.
cat <<XML > associations.wxs
<?xml version='1.0' encoding='utf-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Fragment>
<DirectoryRef Id='INSTALLDIR'>
<Component Id='FileAssociations' Guid='*'>
<RegistryKey Root='HKCR' Key='dawn.dn'>
<RegistryValue Type='string' Value='$description' />
<RegistryValue Type='string' Key='DefaultIcon'
Value='[INSTALLDIR]dn.ico' />
<RegistryValue Type='string' Key='shell\\open\\command'
Value='"[INSTALLDIR]dn.exe" "%1"' />
</RegistryKey>
$(wine "$destdir/dn.exe" --list-supported-extensions \
| sed -n 's/^\*\(\.[[:alnum:]]\{1,\}\)\r\?$/\1/p' \
| tr A-Z a-z \
| sort -u \
| while IFS= read -r ext
do cat <<END
<RegistryKey Root='HKCR' Key='$ext\\OpenWithProgids'>
<RegistryValue Type='string' Name='dawn.dn' Value='' />
</RegistryKey>
END
done)
</Component>
</DirectoryRef>
</Fragment>
</Wix>
XML
wixl --verbose --arch "$arch" -D SourceDir="$destdir" --ext ui \
--output "$msi" "$wxs" package-files.wxs associations.wxs