mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2026-02-23 03:33:57 +11:00
core: add preprocessor for versioning
Some checks failed
Build / Nix (push) Has been cancelled
Build / Nix-1 (push) Has been cancelled
Build / Nix-2 (push) Has been cancelled
Build / Nix-3 (push) Has been cancelled
Build / Nix-4 (push) Has been cancelled
Build / Nix-5 (push) Has been cancelled
Build / Nix-6 (push) Has been cancelled
Build / Nix-7 (push) Has been cancelled
Build / Nix-8 (push) Has been cancelled
Build / Nix-9 (push) Has been cancelled
Build / Nix-10 (push) Has been cancelled
Build / Nix-11 (push) Has been cancelled
Build / Nix-12 (push) Has been cancelled
Build / Nix-13 (push) Has been cancelled
Build / Nix-14 (push) Has been cancelled
Build / Nix-15 (push) Has been cancelled
Build / Nix-16 (push) Has been cancelled
Build / Nix-17 (push) Has been cancelled
Build / Nix-18 (push) Has been cancelled
Build / Nix-19 (push) Has been cancelled
Build / Nix-20 (push) Has been cancelled
Build / Nix-21 (push) Has been cancelled
Build / Nix-22 (push) Has been cancelled
Build / Nix-23 (push) Has been cancelled
Build / Nix-24 (push) Has been cancelled
Build / Nix-25 (push) Has been cancelled
Build / Nix-26 (push) Has been cancelled
Build / Nix-27 (push) Has been cancelled
Build / Nix-28 (push) Has been cancelled
Build / Nix-29 (push) Has been cancelled
Build / Nix-30 (push) Has been cancelled
Build / Nix-31 (push) Has been cancelled
Build / Nix-32 (push) Has been cancelled
Build / Nix-33 (push) Has been cancelled
Build / Archlinux (push) Has been cancelled
Lint / Lint (push) Has been cancelled
Some checks failed
Build / Nix (push) Has been cancelled
Build / Nix-1 (push) Has been cancelled
Build / Nix-2 (push) Has been cancelled
Build / Nix-3 (push) Has been cancelled
Build / Nix-4 (push) Has been cancelled
Build / Nix-5 (push) Has been cancelled
Build / Nix-6 (push) Has been cancelled
Build / Nix-7 (push) Has been cancelled
Build / Nix-8 (push) Has been cancelled
Build / Nix-9 (push) Has been cancelled
Build / Nix-10 (push) Has been cancelled
Build / Nix-11 (push) Has been cancelled
Build / Nix-12 (push) Has been cancelled
Build / Nix-13 (push) Has been cancelled
Build / Nix-14 (push) Has been cancelled
Build / Nix-15 (push) Has been cancelled
Build / Nix-16 (push) Has been cancelled
Build / Nix-17 (push) Has been cancelled
Build / Nix-18 (push) Has been cancelled
Build / Nix-19 (push) Has been cancelled
Build / Nix-20 (push) Has been cancelled
Build / Nix-21 (push) Has been cancelled
Build / Nix-22 (push) Has been cancelled
Build / Nix-23 (push) Has been cancelled
Build / Nix-24 (push) Has been cancelled
Build / Nix-25 (push) Has been cancelled
Build / Nix-26 (push) Has been cancelled
Build / Nix-27 (push) Has been cancelled
Build / Nix-28 (push) Has been cancelled
Build / Nix-29 (push) Has been cancelled
Build / Nix-30 (push) Has been cancelled
Build / Nix-31 (push) Has been cancelled
Build / Nix-32 (push) Has been cancelled
Build / Nix-33 (push) Has been cancelled
Build / Archlinux (push) Has been cancelled
Lint / Lint (push) Has been cancelled
This commit is contained in:
parent
d03c59768c
commit
5eb6f51f4a
12 changed files with 209 additions and 40 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
cmake_minimum_required(VERSION 3.20)
|
cmake_minimum_required(VERSION 3.20)
|
||||||
project(quickshell VERSION "0.2.1" LANGUAGES CXX C)
|
project(quickshell VERSION "0.2.1" LANGUAGES CXX C)
|
||||||
|
|
||||||
|
set(UNRELEASED_FEATURES)
|
||||||
|
|
||||||
set(QT_MIN_VERSION "6.6.0")
|
set(QT_MIN_VERSION "6.6.0")
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ set shell id.
|
||||||
- Added initial support for network management.
|
- Added initial support for network management.
|
||||||
- Added support for grabbing focus from popup windows.
|
- Added support for grabbing focus from popup windows.
|
||||||
- Added support for IPC signal listeners.
|
- Added support for IPC signal listeners.
|
||||||
|
- Added Quickshell version checking and version gated preprocessing.
|
||||||
|
|
||||||
## Other Changes
|
## Other Changes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// NOLINTBEGIN
|
// NOLINTBEGIN
|
||||||
|
#define QS_VERSION "@quickshell_VERSION@"
|
||||||
|
#define QS_VERSION_MAJOR @quickshell_VERSION_MAJOR@
|
||||||
|
#define QS_VERSION_MINOR @quickshell_VERSION_MINOR@
|
||||||
|
#define QS_VERSION_PATCH @quickshell_VERSION_PATCH@
|
||||||
|
#define QS_UNRELEASED_FEATURES "@UNRELEASED_FEATURES@"
|
||||||
#define GIT_REVISION "@GIT_REVISION@"
|
#define GIT_REVISION "@GIT_REVISION@"
|
||||||
#define DISTRIBUTOR "@DISTRIBUTOR@"
|
#define DISTRIBUTOR "@DISTRIBUTOR@"
|
||||||
#define DISTRIBUTOR_DEBUGINFO_AVAILABLE @DEBUGINFO_AVAILABLE@
|
#define DISTRIBUTOR_DEBUGINFO_AVAILABLE @DEBUGINFO_AVAILABLE@
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ qt_add_library(quickshell-core STATIC
|
||||||
singleton.cpp
|
singleton.cpp
|
||||||
generation.cpp
|
generation.cpp
|
||||||
scan.cpp
|
scan.cpp
|
||||||
|
scanenv.cpp
|
||||||
qsintercept.cpp
|
qsintercept.cpp
|
||||||
incubator.cpp
|
incubator.cpp
|
||||||
lazyloader.cpp
|
lazyloader.cpp
|
||||||
|
|
@ -51,7 +52,7 @@ qt_add_qml_module(quickshell-core
|
||||||
|
|
||||||
install_qml_module(quickshell-core)
|
install_qml_module(quickshell-core)
|
||||||
|
|
||||||
target_link_libraries(quickshell-core PRIVATE Qt::Quick Qt::QuickPrivate Qt::Widgets)
|
target_link_libraries(quickshell-core PRIVATE Qt::Quick Qt::QuickPrivate Qt::Widgets quickshell-build)
|
||||||
|
|
||||||
qs_module_pch(quickshell-core SET large)
|
qs_module_pch(quickshell-core SET large)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include "paths.hpp"
|
#include "paths.hpp"
|
||||||
#include "qmlscreen.hpp"
|
#include "qmlscreen.hpp"
|
||||||
#include "rootwrapper.hpp"
|
#include "rootwrapper.hpp"
|
||||||
|
#include "scanenv.hpp"
|
||||||
|
|
||||||
QuickshellSettings::QuickshellSettings() {
|
QuickshellSettings::QuickshellSettings() {
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
|
|
@ -313,6 +314,14 @@ QString QuickshellGlobal::iconPath(const QString& icon, const QString& fallback)
|
||||||
return IconImageProvider::requestString(icon, "", fallback);
|
return IconImageProvider::requestString(icon, "", fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QuickshellGlobal::hasVersion(qint32 major, qint32 minor, const QStringList& features) {
|
||||||
|
return qs::scan::env::PreprocEnv::hasVersion(major, minor, features);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QuickshellGlobal::hasVersion(qint32 major, qint32 minor) {
|
||||||
|
return QuickshellGlobal::hasVersion(major, minor, QStringList());
|
||||||
|
}
|
||||||
|
|
||||||
QuickshellGlobal* QuickshellGlobal::create(QQmlEngine* engine, QJSEngine* /*unused*/) {
|
QuickshellGlobal* QuickshellGlobal::create(QQmlEngine* engine, QJSEngine* /*unused*/) {
|
||||||
auto* qsg = new QuickshellGlobal();
|
auto* qsg = new QuickshellGlobal();
|
||||||
auto* generation = EngineGeneration::findEngineGeneration(engine);
|
auto* generation = EngineGeneration::findEngineGeneration(engine);
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,21 @@ public:
|
||||||
///
|
///
|
||||||
/// The popup can also be blocked by setting `QS_NO_RELOAD_POPUP=1`.
|
/// The popup can also be blocked by setting `QS_NO_RELOAD_POPUP=1`.
|
||||||
Q_INVOKABLE void inhibitReloadPopup() { this->mInhibitReloadPopup = true; }
|
Q_INVOKABLE void inhibitReloadPopup() { this->mInhibitReloadPopup = true; }
|
||||||
|
/// Check if Quickshell's version is at least `major.minor` and the listed
|
||||||
|
/// unreleased features are available. If Quickshell is newer than the given version
|
||||||
|
/// it is assumed that all unreleased features are present. The unreleased feature list
|
||||||
|
/// may be omitted.
|
||||||
|
///
|
||||||
|
/// > [!NOTE] You can feature gate code blocks using Quickshell's preprocessor which
|
||||||
|
/// > has the same function available.
|
||||||
|
/// >
|
||||||
|
/// > ```qml
|
||||||
|
/// > //@ if hasVersion(0, 3, ["feature"])
|
||||||
|
/// > ...
|
||||||
|
/// > //@ endif
|
||||||
|
/// > ```
|
||||||
|
Q_INVOKABLE static bool hasVersion(qint32 major, qint32 minor, const QStringList& features);
|
||||||
|
Q_INVOKABLE static bool hasVersion(qint32 major, qint32 minor);
|
||||||
|
|
||||||
void clearReloadPopupInhibit() { this->mInhibitReloadPopup = false; }
|
void clearReloadPopupInhibit() { this->mInhibitReloadPopup = false; }
|
||||||
[[nodiscard]] bool isReloadPopupInhibited() const { return this->mInhibitReloadPopup; }
|
[[nodiscard]] bool isReloadPopupInhibited() const { return this->mInhibitReloadPopup; }
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,6 @@ void RootWrapper::reloadGraph(bool hard) {
|
||||||
qs::core::QmlToolingSupport::updateTooling(rootPath, scanner);
|
qs::core::QmlToolingSupport::updateTooling(rootPath, scanner);
|
||||||
this->configDirWatcher.addPath(rootPath.path());
|
this->configDirWatcher.addPath(rootPath.path());
|
||||||
|
|
||||||
auto* generation = new EngineGeneration(rootPath, std::move(scanner));
|
|
||||||
generation->wrapper = this;
|
|
||||||
|
|
||||||
// todo: move into EngineGeneration
|
// todo: move into EngineGeneration
|
||||||
if (this->generation != nullptr) {
|
if (this->generation != nullptr) {
|
||||||
qInfo() << "Reloading configuration...";
|
qInfo() << "Reloading configuration...";
|
||||||
|
|
@ -74,6 +71,33 @@ void RootWrapper::reloadGraph(bool hard) {
|
||||||
|
|
||||||
QDir::setCurrent(this->originalWorkingDirectory);
|
QDir::setCurrent(this->originalWorkingDirectory);
|
||||||
|
|
||||||
|
if (!scanner.scanErrors.isEmpty()) {
|
||||||
|
qCritical() << "Failed to load configuration";
|
||||||
|
QString errorString = "Failed to load configuration";
|
||||||
|
for (auto& error: scanner.scanErrors) {
|
||||||
|
const auto& file = error.file;
|
||||||
|
QString rel;
|
||||||
|
if (file.startsWith(rootPath.path() % '/')) {
|
||||||
|
rel = '@' % file.sliced(rootPath.path().length() + 1);
|
||||||
|
} else {
|
||||||
|
rel = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto msg = " error in " % rel % '[' % QString::number(error.line) % ":0]: " % error.message;
|
||||||
|
errorString += '\n' % msg;
|
||||||
|
qCritical().noquote() << msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->generation != nullptr && this->generation->qsgInstance != nullptr) {
|
||||||
|
emit this->generation->qsgInstance->reloadFailed(errorString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* generation = new EngineGeneration(rootPath, std::move(scanner));
|
||||||
|
generation->wrapper = this;
|
||||||
|
|
||||||
QUrl url;
|
QUrl url;
|
||||||
url.setScheme("qs");
|
url.setScheme("qs");
|
||||||
url.setPath("@/qs/" % rootFile.fileName());
|
url.setPath("@/qs/" % rootFile.fileName());
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
#include "scan.hpp"
|
#include "scan.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qfileinfo.h>
|
#include <qfileinfo.h>
|
||||||
|
#include <qjsengine.h>
|
||||||
#include <qjsonarray.h>
|
#include <qjsonarray.h>
|
||||||
#include <qjsondocument.h>
|
#include <qjsondocument.h>
|
||||||
#include <qjsonobject.h>
|
#include <qjsonobject.h>
|
||||||
|
|
@ -15,6 +17,7 @@
|
||||||
#include <qtextstream.h>
|
#include <qtextstream.h>
|
||||||
|
|
||||||
#include "logcat.hpp"
|
#include "logcat.hpp"
|
||||||
|
#include "scanenv.hpp"
|
||||||
|
|
||||||
QS_LOGGING_CATEGORY(logQmlScanner, "quickshell.qmlscanner", QtWarningMsg);
|
QS_LOGGING_CATEGORY(logQmlScanner, "quickshell.qmlscanner", QtWarningMsg);
|
||||||
|
|
||||||
|
|
@ -115,12 +118,30 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
|
||||||
auto stream = QTextStream(&file);
|
auto stream = QTextStream(&file);
|
||||||
auto imports = QVector<QString>();
|
auto imports = QVector<QString>();
|
||||||
|
|
||||||
|
bool inHeader = false;
|
||||||
|
auto ifScopes = QVector<bool>();
|
||||||
|
bool sourceMasked = false;
|
||||||
|
int lineNum = 0;
|
||||||
|
QString overrideText;
|
||||||
|
bool isOverridden = false;
|
||||||
|
|
||||||
|
auto pragmaEngine = QJSEngine();
|
||||||
|
pragmaEngine.globalObject().setPrototype(
|
||||||
|
pragmaEngine.newQObject(new qs::scan::env::PreprocEnv())
|
||||||
|
);
|
||||||
|
|
||||||
|
auto postError = [&, this](QString error) {
|
||||||
|
this->scanErrors.append({.file = path, .message = std::move(error), .line = lineNum});
|
||||||
|
};
|
||||||
|
|
||||||
while (!stream.atEnd()) {
|
while (!stream.atEnd()) {
|
||||||
auto line = stream.readLine().trimmed();
|
++lineNum;
|
||||||
|
bool hideMask = false;
|
||||||
|
auto rawLine = stream.readLine();
|
||||||
|
auto line = rawLine.trimmed();
|
||||||
|
if (!sourceMasked && inHeader) {
|
||||||
if (!singleton && line == "pragma Singleton") {
|
if (!singleton && line == "pragma Singleton") {
|
||||||
singleton = true;
|
singleton = true;
|
||||||
} else if (!internal && line == "//@ pragma Internal") {
|
|
||||||
internal = true;
|
|
||||||
} else if (line.startsWith("import")) {
|
} else if (line.startsWith("import")) {
|
||||||
// we dont care about "import qs" as we always load the root folder
|
// we dont care about "import qs" as we always load the root folder
|
||||||
if (auto importCursor = line.indexOf(" qs."); importCursor != -1) {
|
if (auto importCursor = line.indexOf(" qs."); importCursor != -1) {
|
||||||
|
|
@ -153,13 +174,57 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
|
||||||
auto name = line.sliced(startQuot + 1, endQuot - startQuot - 1);
|
auto name = line.sliced(startQuot + 1, endQuot - startQuot - 1);
|
||||||
imports.push_back(name);
|
imports.push_back(name);
|
||||||
}
|
}
|
||||||
} else if (line.contains('{')) break;
|
} else if (!internal && line == "//@ pragma Internal") {
|
||||||
|
internal = true;
|
||||||
|
} else if (line.contains('{')) {
|
||||||
|
inHeader = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.startsWith("//@ if ")) {
|
||||||
|
auto code = line.sliced(7);
|
||||||
|
auto value = pragmaEngine.evaluate(code, path, 1234);
|
||||||
|
bool mask = true;
|
||||||
|
|
||||||
|
if (value.isError()) {
|
||||||
|
postError(QString("Evaluating if: %0").arg(value.toString()));
|
||||||
|
} else if (!value.isBool()) {
|
||||||
|
postError(QString("If expression \"%0\" is not a boolean").arg(value.toString()));
|
||||||
|
} else if (value.toBool()) {
|
||||||
|
mask = false;
|
||||||
|
}
|
||||||
|
if (!sourceMasked && mask) hideMask = true;
|
||||||
|
mask = sourceMasked || mask; // cant unmask if a nested if passes
|
||||||
|
ifScopes.append(mask);
|
||||||
|
if (mask) isOverridden = true;
|
||||||
|
sourceMasked = mask;
|
||||||
|
} else if (line.startsWith("//@ endif")) {
|
||||||
|
if (ifScopes.isEmpty()) {
|
||||||
|
postError("endif without matching if");
|
||||||
|
} else {
|
||||||
|
ifScopes.pop_back();
|
||||||
|
|
||||||
|
if (ifScopes.isEmpty()) sourceMasked = false;
|
||||||
|
else sourceMasked = ifScopes.last();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hideMask && sourceMasked) overrideText.append("// MASKED: " % rawLine % '\n');
|
||||||
|
else overrideText.append(rawLine % '\n');
|
||||||
|
|
||||||
next:;
|
next:;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ifScopes.isEmpty()) {
|
||||||
|
postError("unclosed preprocessor if block");
|
||||||
|
}
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
if (isOverridden) {
|
||||||
|
this->fileIntercepts.insert(path, overrideText);
|
||||||
|
}
|
||||||
|
|
||||||
if (logQmlScanner().isDebugEnabled() && !imports.isEmpty()) {
|
if (logQmlScanner().isDebugEnabled() && !imports.isEmpty()) {
|
||||||
qCDebug(logQmlScanner) << "Found imports" << imports;
|
qCDebug(logQmlScanner) << "Found imports" << imports;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@ public:
|
||||||
QVector<QString> scannedFiles;
|
QVector<QString> scannedFiles;
|
||||||
QHash<QString, QString> fileIntercepts;
|
QHash<QString, QString> fileIntercepts;
|
||||||
|
|
||||||
|
struct ScanError {
|
||||||
|
QString file;
|
||||||
|
QString message;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
|
||||||
|
QVector<ScanError> scanErrors;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDir rootPath;
|
QDir rootPath;
|
||||||
|
|
||||||
|
|
|
||||||
22
src/core/scanenv.cpp
Normal file
22
src/core/scanenv.cpp
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include "scanenv.hpp"
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
|
||||||
|
#include "build.hpp"
|
||||||
|
|
||||||
|
namespace qs::scan::env {
|
||||||
|
|
||||||
|
bool PreprocEnv::hasVersion(int major, int minor, const QStringList& features) {
|
||||||
|
if (QS_VERSION_MAJOR > major) return true;
|
||||||
|
if (QS_VERSION_MAJOR == major && QS_VERSION_MINOR > minor) return true;
|
||||||
|
|
||||||
|
auto availFeatures = QString(QS_UNRELEASED_FEATURES).split(';');
|
||||||
|
|
||||||
|
for (const auto& feature: features) {
|
||||||
|
if (!availFeatures.contains(feature)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QS_VERSION_MAJOR == major && QS_VERSION_MINOR == minor;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::scan::env
|
||||||
17
src/core/scanenv.hpp
Normal file
17
src/core/scanenv.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
namespace qs::scan::env {
|
||||||
|
|
||||||
|
class PreprocEnv: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE static bool
|
||||||
|
hasVersion(int major, int minor, const QStringList& features = QStringList());
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::scan::env
|
||||||
|
|
@ -519,8 +519,8 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.misc.printVersion) {
|
if (state.misc.printVersion) {
|
||||||
qCInfo(logBare).noquote().nospace()
|
qCInfo(logBare).noquote().nospace() << "quickshell " << QS_VERSION << ", revision "
|
||||||
<< "quickshell 0.2.1, revision " << GIT_REVISION << ", distributed by: " << DISTRIBUTOR;
|
<< GIT_REVISION << ", distributed by: " << DISTRIBUTOR;
|
||||||
|
|
||||||
if (state.log.verbosity > 1) {
|
if (state.log.verbosity > 1) {
|
||||||
qCInfo(logBare).noquote() << "\nBuildtime Qt Version:" << QT_VERSION_STR;
|
qCInfo(logBare).noquote() << "\nBuildtime Qt Version:" << QT_VERSION_STR;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue