fix qrc generation for system with symlinks and always build the library

on e.g. macOS symlinks survived the cmake dir copy
and the rcc compiler just did not add the symlinked files

now we handle both symlinks and windows pseudo link files
the same way

always generate the icon library, we will require it
in icon themes to ensure we are no longer missing icons
on foreign desktop envs

ensures that the CI tests the code path we did only
take in the patched craft builds
This commit is contained in:
Christoph Cullmann 2024-05-09 11:28:53 +00:00
parent ae5c4ffb1b
commit f6dd35b261
5 changed files with 79 additions and 113 deletions

View File

@ -64,59 +64,13 @@ add_feature_info("Icon generation" ${WITH_ICON_GENERATION} "for 24x24 and symbol
This feature requires Python 3 and the lxml Python 3 module." This feature requires Python 3 and the lxml Python 3 module."
) # The exact amount of indentation used in the line(s) above is intentional ) # The exact amount of indentation used in the line(s) above is intentional
# don't install the individual icons
# useful if you only want to link with the generated icon library
option(SKIP_INSTALL_ICONS "Skip installing the icons files" OFF) option(SKIP_INSTALL_ICONS "Skip installing the icons files" OFF)
option(ICONS_LIBRARY "Install a library including the breeze & breeze dark icons as resources & functions to use them. This is intended for self contained deployments and should generally not be enabled for systems that have the concept of icon themes like Linux distributions/*BSD" OFF)
if(ICONS_LIBRARY) find_package(Qt6 NO_MODULE REQUIRED Core)
find_package(Qt6 NO_MODULE REQUIRED Core) add_executable(qrcAlias qrcAlias.cpp)
add_executable(qrcAlias qrcAlias.cpp) target_link_libraries(qrcAlias PUBLIC Qt6::Core)
target_link_libraries(qrcAlias PUBLIC Qt6::Core)
function(generate_binary_resource target outfile)
set(RESOURCES_WORKING_DIR ${CMAKE_CURRENT_BINARY_DIR}/res)
set(RESOURCE_FILE ${RESOURCES_WORKING_DIR}/breeze-${target}.qrc)
set(BINARY_RESOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/breeze-${target}.rcc)
# Use $<IF:$<BOOL:${MSVC}>,PATH,LD_LIBRARY_PATH> instead of ${pathVarName} once CMake 3.8 is minimum
if(MSVC)
set(pathVarName PATH)
else()
set(pathVarName LD_LIBRARY_PATH)
endif()
get_target_property(QT_RCC_EXECUTABLE Qt6::rcc LOCATION)
add_custom_target(breeze-${target}-mkdir
COMMAND ${CMAKE_COMMAND} -E make_directory ${RESOURCES_WORKING_DIR}
)
add_custom_command(OUTPUT ${RESOURCE_FILE}
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR} ${RESOURCES_WORKING_DIR}
COMMAND ${CMAKE_COMMAND} -E remove
${RESOURCE_FILE}
${RESOURCE_FILE}.depends
${RESOURCES_WORKING_DIR}/.gitignore
${RESOURCES_WORKING_DIR}/CMakeLists.txt
COMMAND ${QT_RCC_EXECUTABLE} --project -o ${CMAKE_CURRENT_BINARY_DIR}/tmp.qrc
COMMAND ${CMAKE_COMMAND} -E env
${pathVarName}=$<TARGET_FILE_DIR:Qt6::Core>
$<TARGET_FILE:qrcAlias> -i ${CMAKE_CURRENT_BINARY_DIR}/tmp.qrc -o ${RESOURCE_FILE}
WORKING_DIRECTORY ${RESOURCES_WORKING_DIR}
DEPENDS breeze-${target}-mkdir
)
add_custom_command(OUTPUT ${BINARY_RESOURCE_FILE}
COMMAND ${QT_RCC_EXECUTABLE} --binary
-o ${BINARY_RESOURCE_FILE}
${RESOURCE_FILE}
DEPENDS ${RESOURCE_FILE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(breeze-${target}-rcc ALL DEPENDS ${BINARY_RESOURCE_FILE})
set(${outfile} ${BINARY_RESOURCE_FILE} PARENT_SCOPE)
endfunction()
endif()
if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING)
# validate # validate
@ -163,21 +117,18 @@ ecm_setup_version(PROJECT
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF6BreezeIconsConfigVersion.cmake" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF6BreezeIconsConfigVersion.cmake"
SOVERSION 6) SOVERSION 6)
# shall we create a library with the icons? find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
if(ICONS_LIBRARY)
find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
install(EXPORT KF6BreezeIconsTargets install(EXPORT KF6BreezeIconsTargets
DESTINATION "${CMAKECONFIG_INSTALL_DIR}" DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
FILE KF6BreezeIconsTargets.cmake FILE KF6BreezeIconsTargets.cmake
NAMESPACE KF6:: NAMESPACE KF6::
) )
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/breezeicons_version.h install(FILES ${CMAKE_CURRENT_BINARY_DIR}/breezeicons_version.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/BreezeIcons COMPONENT Devel) DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/BreezeIcons COMPONENT Devel)
add_subdirectory(src) add_subdirectory(src)
endif()
include(ECMFeatureSummary) include(ECMFeatureSummary)
ecm_feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES INCLUDE_QUIET_PACKAGES) ecm_feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES INCLUDE_QUIET_PACKAGES)

View File

@ -1,7 +1,5 @@
@PACKAGE_INIT@ @PACKAGE_INIT@
if(@ICONS_LIBRARY@) include(CMakeFindDependencyMacro)
include(CMakeFindDependencyMacro) find_dependency(Qt6Gui "@REQUIRED_QT_VERSION@")
find_dependency(Qt6Gui "@REQUIRED_QT_VERSION@") include("${CMAKE_CURRENT_LIST_DIR}/KF6BreezeIconsTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/KF6BreezeIconsTargets.cmake")
endif()

View File

@ -1,7 +1,3 @@
if(ICONS_LIBRARY)
generate_binary_resource(icons binary_resource)
endif()
########### install files ############### ########### install files ###############
FILE(GLOB possible_icon_dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) FILE(GLOB possible_icon_dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)

View File

@ -19,6 +19,8 @@
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QRegularExpression> #include <QRegularExpression>
@ -28,13 +30,12 @@ static QString link(const QString &path, const QString &fileName)
{ {
QFile in(path + QLatin1Char('/') + fileName); QFile in(path + QLatin1Char('/') + fileName);
if (!in.open(QIODevice::ReadOnly)) { if (!in.open(QIODevice::ReadOnly)) {
qWarning() << "failed to read" << path << fileName << in.fileName(); qFatal() << "failed to open" << path << fileName << in.fileName();
return QString(); return QString();
} }
QString firstLine = QString::fromLocal8Bit(in.readLine()); QString firstLine = QString::fromLocal8Bit(in.readLine());
if (firstLine.isEmpty()) { if (firstLine.isEmpty()) {
qWarning() << in.fileName() << "line could not be read...";
return QString(); return QString();
} }
QRegularExpression fNameReg(QStringLiteral("(.*\\.(?:svg|png|gif|ico))$")); QRegularExpression fNameReg(QStringLiteral("(.*\\.(?:svg|png|gif|ico))$"));
@ -53,43 +54,55 @@ static QString link(const QString &path, const QString &fileName)
return path + QLatin1Char('/') + match.captured(1); return path + QLatin1Char('/') + match.captured(1);
} }
static int parseFile(const QString &infile, const QString &outfile) static int parseFile(const QString &indir, const QString &outfile)
{ {
QFile in(infile);
QFile out(outfile); QFile out(outfile);
QRegularExpression imageReg(QStringLiteral("<file>(.*\\.(?:svg|png|gif|ico))</file>"));
if (!in.open(QIODevice::ReadOnly)) {
qWarning() << "Failed to open" << infile;
return -1;
}
if (!out.open(QIODevice::WriteOnly)) { if (!out.open(QIODevice::WriteOnly)) {
qWarning() << "Failed to create" << outfile; qFatal() << "Failed to create" << outfile;
return -2; }
out.write("<!DOCTYPE RCC><RCC version=\"1.0\">\n");
out.write("<qresource>\n");
// go to input dir to have proper relative paths
if (!QDir::setCurrent(indir)) {
qFatal() << "Failed to switch to input directory" << indir;
} }
while (in.bytesAvailable()) { // we look at all interesting files in the indir and create a qrc with resolved symlinks
QString line = QString::fromLocal8Bit(in.readLine()); QDirIterator it(QStringLiteral("."),
QRegularExpressionMatch match = imageReg.match(line); {QStringLiteral("*.theme"), QStringLiteral("*.svg"), QStringLiteral("*.png"), QStringLiteral("*.gif"), QStringLiteral("*.ico")},
if (!match.hasMatch()) { QDir::Files,
// qDebug() << "No Match: " << line; QDirIterator::Subdirectories);
out.write(qPrintable(line)); while (it.hasNext()) {
continue; // ensure nice path without ./ and Co.
const auto file = QDir::current().relativeFilePath(it.next());
const QFileInfo fileInfo(file);
// real symlink resolving for Unices, the rcc compiler ignores such files in -project mode
if (fileInfo.isSymLink()) {
const auto linkPath = fileInfo.canonicalFilePath();
if (linkPath.isEmpty()) {
qFatal() << "Broken symlink" << file << "in input directory" << indir;
}
QString newLine = QStringLiteral(" <file alias=\"%1\">%2</file>\n").arg(file, QDir::current().relativeFilePath(linkPath));
out.write(newLine.toUtf8());
} }
QFileInfo info(match.captured(1)); // pseudo link files generated by Git on Windows
else if (const auto aliasLink = link(fileInfo.path(), fileInfo.fileName()); !aliasLink.isEmpty()) {
QString aliasLink = link(info.path(), info.fileName()); QString newLine = QStringLiteral(" <file alias=\"%1\">%2</file>\n").arg(file, QDir::current().relativeFilePath(aliasLink));
if (aliasLink.isEmpty()) { out.write(newLine.toUtf8());
// qDebug() << "No alias: " << line;
out.write(qPrintable(line));
continue;
} }
QString newLine = QStringLiteral("<file alias=\"%1\">%2</file>\n").arg(match.captured(1), aliasLink); // normal file
// qDebug() << newLine; else {
out.write(qPrintable(newLine)); QString newLine = QStringLiteral(" <file>%1</file>\n").arg(file);
out.write(newLine.toUtf8());
} }
}
out.write("</qresource>\n");
out.write("</RCC>\n");
return 0; return 0;
} }
@ -99,13 +112,9 @@ int main(int argc, char *argv[])
QCommandLineParser parser; QCommandLineParser parser;
QCommandLineOption inOption(QStringList() << QLatin1String("i") << QLatin1String("infile"), QStringLiteral("Input qrc file"), QStringLiteral("infile")); QCommandLineOption inOption(QStringList() << QLatin1String("i") << QLatin1String("indir"), QStringLiteral("Input directory"), QStringLiteral("indir"));
QCommandLineOption outOption(QStringList() << QLatin1String("o") << QLatin1String("outfile"), QStringLiteral("Output qrc file"), QStringLiteral("outfile")); QCommandLineOption outOption(QStringList() << QLatin1String("o") << QLatin1String("outfile"), QStringLiteral("Output qrc file"), QStringLiteral("outfile"));
parser.setApplicationDescription( parser.setApplicationDescription(QLatin1String("Create a resource file from the given input directory handling symlinks and pseudo symlink files."));
QLatin1String("On Windows git handles symbolic links by converting them "
"to text files containing the links to the actual file. This application "
"takes a .qrc file as input and outputs a .qrc file with the symbolic "
"links converted to qrc-aliases."));
parser.addHelpOption(); parser.addHelpOption();
parser.addVersionOption(); parser.addVersionOption();
parser.addOption(inOption); parser.addOption(inOption);
@ -114,6 +123,5 @@ int main(int argc, char *argv[])
const QString inName = parser.value(inOption); const QString inName = parser.value(inOption);
const QString outName = parser.value(outOption); const QString outName = parser.value(outOption);
return parseFile(inName, outName); return parseFile(inName, outName);
} }

View File

@ -1,8 +1,21 @@
# helper functions to ensure we load the icon theme # helper functions to ensure we load the icon theme
set(kbreezeicons_SRCS breezeicons.cpp) set(kbreezeicons_SRCS breezeicons.cpp)
# generate resource file for all breeze icons
set(ICON_SRC_DIR ${CMAKE_SOURCE_DIR}/icons)
set(RESOURCES_WORKING_DIR ${CMAKE_CURRENT_BINARY_DIR}/res)
set(RESOURCE_FILE ${RESOURCES_WORKING_DIR}/breeze-icons.qrc)
# we only will use the normal icons, we do recoloring later
add_custom_command(OUTPUT ${RESOURCE_FILE}
COMMAND ${CMAKE_COMMAND} -E make_directory ${RESOURCES_WORKING_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_directory ${ICON_SRC_DIR} ${RESOURCES_WORKING_DIR}
COMMAND qrcAlias -i ${ICON_SRC_DIR} -o ${RESOURCE_FILE}
DEPENDS qrcAlias
)
qt_add_big_resources(kbreezeicons_resource_SRCS qt_add_big_resources(kbreezeicons_resource_SRCS
${CMAKE_BINARY_DIR}/icons/res/breeze-icons.qrc ${RESOURCE_FILE}
OPTIONS --root /icons/breeze OPTIONS --root /icons/breeze
) )