qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,133 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "accessibilityinspector.h"
#include "screenreader.h"
#include "optionswidget.h"
#include "accessibilityscenemanager.h"
#include <QtDeclarative/QtDeclarative>
void MouseInterceptingGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
emit mousePressed(event->scenePos().toPoint());
QGraphicsScene::mousePressEvent(event);
}
void MouseInterceptingGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
emit mouseDobleClicked();
QGraphicsScene::mouseDoubleClickEvent(event);
}
AccessibilitySceneManager *sceneManager = 0;
QAccessible::UpdateHandler previousUpdateHandler = 0;
bool updateHandlerRecursion = false;
void accessibilityUpdateHandler(QAccessibleEvent *event)
{
if (updateHandlerRecursion)
return;
updateHandlerRecursion = true;
if (sceneManager) {
sceneManager->handleUpdate(event);
//qDebug() << "update";
}
if (previousUpdateHandler) // call prev just to be sure.
previousUpdateHandler(event);
updateHandlerRecursion = false;
}
AccessibilityInspector::AccessibilityInspector(QObject *parent) :
QObject(parent)
{
}
AccessibilityInspector::~AccessibilityInspector()
{
delete optionsWidget;
delete accessibilityScene;
delete accessibilityView;
delete accessibilityTreeScene;
delete accessibilityTreeView;
delete screenReader;
}
void AccessibilityInspector::inspectWindow(QWindow *window)
{
qDebug() << "AccessibilityInspector::inspectWindow()" << window;
if (window->parent() || window->transientParent())
return;
optionsWidget = new OptionsWidget();
accessibilityScene = new MouseInterceptingGraphicsScene();
accessibilityView = new QGraphicsView();
accessibilityView->setScene(accessibilityScene);
accessibilityView->resize(640, 480);
accessibilityView->scale(1.3, 1.3);
accessibilityTreeScene = new QGraphicsScene();
accessibilityTreeView = new QGraphicsView();
accessibilityTreeView->setScene(accessibilityTreeScene);
accessibilityTreeView->resize(640, 480);
sceneManager = new AccessibilitySceneManager();
QObject::connect(optionsWidget, SIGNAL(optionsChanged()), sceneManager, SLOT(updateAccessibilitySceneItemFlags()));
QObject::connect(optionsWidget, SIGNAL(refreshClicked()), sceneManager, SLOT(populateAccessibilityScene()));
QObject::connect(optionsWidget, SIGNAL(refreshClicked()), sceneManager, SLOT(populateAccessibilityTreeScene()));
QObject::connect(optionsWidget, SIGNAL(scaleChanged(int)), sceneManager, SLOT(changeScale(int)));
sceneManager->setOptionsWidget(optionsWidget);
sceneManager->setRootWindow(window);
sceneManager->setScene(accessibilityScene);
sceneManager->setView(accessibilityView);
sceneManager->setTreeScene(accessibilityTreeScene);
sceneManager->setTreeView(accessibilityTreeView);
screenReader = new ScreenReader;
QObject::connect(accessibilityScene, SIGNAL(mousePressed(QPoint)), screenReader, SLOT(touchPoint(QPoint)));
QObject::connect(accessibilityScene, SIGNAL(mouseDobleClicked()), screenReader, SLOT(activate()));
QObject::connect(screenReader, SIGNAL(selected(QObject*)), sceneManager, SLOT(setSelected(QObject*)));
screenReader->setRootObject(window);
screenReader->setOptionsWidget(optionsWidget);
previousUpdateHandler = QAccessible::installUpdateHandler(accessibilityUpdateHandler);
QTimer::singleShot(100, sceneManager, SLOT(populateAccessibilityScene()));
QTimer::singleShot(100, sceneManager, SLOT(populateAccessibilityTreeScene()));
QSettings settings;
accessibilityView->restoreGeometry(settings.value("accessiblityGeometry").toByteArray());
accessibilityView->setObjectName(QLatin1String("accessibilityInspectorView"));
accessibilityView->show();
accessibilityTreeView->restoreGeometry(settings.value("treeGeometry").toByteArray());
accessibilityTreeView->setObjectName(QLatin1String("accessibilityInspectorTreeView"));
accessibilityTreeView->show();
optionsWidget->restoreGeometry(settings.value("optionsGeometry").toByteArray());
optionsWidget->setObjectName(QLatin1String("accessibilityInspectorOptions"));
optionsWidget->show();
}
void AccessibilityInspector::saveWindowGeometry()
{
QSettings settings;
settings.setValue("accessiblityGeometry", accessibilityView->saveGeometry());
settings.setValue("treeGeometry", accessibilityTreeView->saveGeometry());
settings.setValue("optionsGeometry", optionsWidget->saveGeometry());
}
QString translateRole(QAccessible::Role role)
{
return qAccessibleRoleString(role);
}

View File

@ -0,0 +1,49 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef ACCESSIBILITYINSPECTOR_H
#define ACCESSIBILITYINSPECTOR_H
#include <QObject>
#include <qgraphicsscene.h>
#include <QAccessible>
QString translateRole(QAccessible::Role role);
class OptionsWidget;
class MouseInterceptingGraphicsScene;
class QGraphicsView;
class QGraphicsScene;
class AccessibilitySceneManager;
class ScreenReader;
class AccessibilityInspector : public QObject
{
Q_OBJECT
public:
explicit AccessibilityInspector(QObject *parent = nullptr);
~AccessibilityInspector();
void inspectWindow(QWindow *window);
void saveWindowGeometry();
signals:
private:
OptionsWidget *optionsWidget;
MouseInterceptingGraphicsScene *accessibilityScene;
QGraphicsView *accessibilityView;
QGraphicsScene *accessibilityTreeScene;
QGraphicsView *accessibilityTreeView;
ScreenReader *screenReader;
};
class MouseInterceptingGraphicsScene : public QGraphicsScene
{
Q_OBJECT
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
signals:
void mousePressed(const QPoint point);
void mouseDobleClicked();
};
#endif // ACCESSIBILITYINSPECTOR_H

View File

@ -0,0 +1,19 @@
QT += declarative
INCLUDEPATH += $$PWD
# DEFINES += ACCESSIBILITYINSPECTOR_NO_UITOOLS
# QT += uitools
HEADERS += \
$$PWD/screenreader.h \
$$PWD/optionswidget.h \
$$PWD/accessibilityscenemanager.h \
$$PWD/accessibilityinspector.h
SOURCES += \
$$PWD/optionswidget.cpp \
$$PWD/accessibilityscenemanager.cpp \
$$PWD/screenreader.cpp \
$$PWD/accessibilityinspector.cpp

View File

@ -0,0 +1,5 @@
include (accessibilityinspector.pri)
SOURCES += main.cpp \
CONFIG += console

View File

@ -0,0 +1,444 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "accessibilityscenemanager.h"
AccessibilitySceneManager::AccessibilitySceneManager()
{
m_window = 0;
m_view = 0;
m_scene = 0;
m_rootItem = 0;
m_optionsWidget = 0;
m_selectedObject = 0;
}
void AccessibilitySceneManager::populateAccessibilityScene()
{
m_scene->clear();
m_graphicsItems.clear();
QAccessibleInterface * rootInterface = m_window->accessibleRoot();
if (!rootInterface)
return;
populateAccessibilityScene(rootInterface, m_scene);
}
void AccessibilitySceneManager::updateAccessibilitySceneItemFlags()
{
qDebug() << "update";
foreach (QObject *object, m_graphicsItems.keys()) {
if (!object)
continue;
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
if (!interface)
continue;
updateItemFlags(m_graphicsItems.value(object), interface);
}
}
void AccessibilitySceneManager::populateAccessibilityTreeScene()
{
m_treeScene->clear();
QAccessibleInterface * rootInterface = m_window->accessibleRoot();
if (!rootInterface) {
qWarning("QWindow::accessibleRoot returned 0");
return;
}
populateAccessibilityTreeScene(rootInterface);
}
void AccessibilitySceneManager::handleUpdate(QAccessibleEvent *event)
{
QObject *object = event->object();
QAccessible::Event type = event->type();
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
if (!interface)
return;
QString name = interface->text(QAccessible::Name);
if (type == QAccessible::ObjectCreated) {
// qDebug() << "ObjectCreated" << object << name;
populateAccessibilityScene(interface, m_scene);
}
QGraphicsRectItem *item = m_graphicsItems.value(object);
if (!item) {
// qDebug() << "populateAccessibilityScene failed for" << object;
return;
}
if (type == QAccessible::LocationChanged) {
//if (name.startsWith("List"))
qDebug() << "locationChange" << object << name << interface->rect();
updateItem(item, interface);
for (int i = 0; i < interface->childCount(); ++i) {
QAccessibleInterface *child = interface->child(i);
if (child) {
updateItem(m_graphicsItems.value(child->object()), child);
}
}
} else if (type == QAccessible::ObjectDestroyed) {
// qDebug() << "ObjectDestroyed" << object << name;
delete m_graphicsItems.value(object);
m_graphicsItems.remove(object);
m_animatedObjects.remove(object);
if (object == m_selectedObject) {
m_selectedObject = 0;
}
} else if (type == QAccessible::ObjectHide) {
// qDebug() << "ObjectCreated Hide" << object;
updateItemFlags(item, interface);
} else if (type == QAccessible::ObjectShow) {
// qDebug() << "ObjectCreated Show" << object;
updateItemFlags(item, interface);
} else if (type == QAccessible::ScrollingStart) {
qDebug() << "ObjectCreated ScrollingStart" << object;
for (int i = 0; i < interface->childCount(); ++i) {
QAccessibleInterface *child = interface->child(i);
if (child) {
m_animatedObjects.insert(child->object());
}
}
} else if (type == QAccessible::ScrollingEnd) {
// qDebug() << "ObjectCreated ScrollingEnd" << object;
foreach (QObject *object, m_animatedObjects) {
updateItem(m_graphicsItems.value(object), interface);
}
m_animatedObjects.clear();
} else {
// qDebug() << "other update" << object;
}
}
void AccessibilitySceneManager::setSelected(QObject *object)
{
m_scene->update(); // scedule update
// clear existing selection
if (m_selectedObject) {
QObject *previousSelectedObject = m_selectedObject;
m_selectedObject = 0;
updateItem(previousSelectedObject);
}
m_selectedObject = object;
updateItem(object);
populateAccessibilityTreeScene();
}
void AccessibilitySceneManager::changeScale(int)
{
// No QGraphicsView::setScale :(
//m_view->scale(scale / 10.0, scale / 10.0);
//if (m_rootItem)
// m_view->ensureVisible(m_rootItem);
}
void AccessibilitySceneManager::updateItems(QObject *root)
{
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(root);
if (!interface)
return;
updateItem(m_graphicsItems.value(root), interface);
for (int i = 0; i < interface->childCount(); ++i) {
QAccessibleInterface *child = interface->child(i);
updateItems(child->object());
}
}
void AccessibilitySceneManager::updateItem(QObject *object)
{
if (!object)
return;
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
if (!interface)
return;
updateItem(m_graphicsItems.value(object), interface);
}
void AccessibilitySceneManager::updateItem(QGraphicsRectItem *item, QAccessibleInterface *interface)
{
if (!item)
return;
QRect rect = interface->rect();
item->setPos(rect.topLeft());
item->setRect(QRect(QPoint(0,0), rect.size()));
updateItemFlags(item, interface);
}
void AccessibilitySceneManager::updateItemFlags(QGraphicsRectItem *item, QAccessibleInterface *interface)
{
// qDebug() << "udpateItemFlags" << interface << interface->object();
bool shouldShow = true;
if (m_optionsWidget->hideInvisibleItems()) {
if (isHidden(interface)) {
shouldShow = false;
}
}
if (m_optionsWidget->hideOffscreenItems()) {
if (interface->state().offscreen) {
shouldShow = false;
}
}
if (m_optionsWidget->hidePaneItems()) {
if (interface->role() & QAccessible::Pane) {
shouldShow = false;
}
}
if (m_optionsWidget->hideNullObjectItems()) {
if (interface->object() == 0) {
shouldShow = false;
}
}
if (m_optionsWidget->hideNullRectItems()) {
if (interface->rect().isNull()) {
shouldShow = false;
}
}
item->setVisible(shouldShow);
if (interface->object() && interface->object() == m_selectedObject)
item->setBrush(QColor(Qt::yellow));
else
item->setBrush(QColor(Qt::white));
m_view->update();
}
QGraphicsRectItem * AccessibilitySceneManager::processInterface(QAccessibleInterface * interface, QGraphicsScene *scene)
{
// Process this interface
QGraphicsRectItem * item = new QGraphicsRectItem();
scene->addItem(item);
if (!m_rootItem)
m_rootItem = item;
QString name = interface->text(QAccessible::Name);
QString description; // = interface->text(QAccessibleInterface::Description, child);
QString role = translateRole(interface->role());
int childCount = interface->childCount();
/* qDebug() << "name:" << name << "local pos" <<
interface->rect(0) << "description" << description << "childCount" << childCount;
*/
updateItem(item, interface);
QGraphicsSimpleTextItem * textItem = new QGraphicsSimpleTextItem();
textItem->setParentItem(item);
textItem->setPos(QPoint(5, 5));
QString text;
text.append("Name: " + name + " ");
if (!description.isEmpty())
text.append("Description: " + description + " ");
text.append("Role: " + role + " ");
if (childCount > 0)
text.append("ChildCount: " + QString::number(childCount) + " ");
textItem->setText(text);
QFont font;
font.setPointSize(10);
// font.setPointSize(14);
textItem->setFont(font);
return item;
}
void AccessibilitySceneManager::populateAccessibilityScene(QAccessibleInterface * interface, QGraphicsScene *scene)
{
if (!interface)
return;
QGraphicsRectItem *item = processInterface(interface, scene);
QObject *object = interface->object();
if (object) {
m_graphicsItems.insert(object, item);
}
for (int i = 0; i < interface->childCount(); ++i) {
QAccessibleInterface *child = interface->child(i);
updateItems(child->object());
populateAccessibilityScene(child, scene);
}
}
AccessibilitySceneManager::TreeItem AccessibilitySceneManager::computeLevels(QAccessibleInterface * interface, int level)
{
if (interface == 0)
return TreeItem();
TreeItem currentLevel;
int usedChildren = 0;
for (int i = 0; i < interface->childCount(); ++i) {
QAccessibleInterface *child = interface->child(i);
if (child != 0) {
++usedChildren;
TreeItem childLevel = computeLevels(child, level + 1);
currentLevel.children.append(childLevel);
currentLevel.width += childLevel.width + m_treeItemHorizontalPadding;
}
}
// leaf node case
if (usedChildren == 0) {
currentLevel.width = m_treeItemWidth + m_treeItemHorizontalPadding;
}
// capture information:
currentLevel.name = interface->text(QAccessible::Name);
currentLevel.description += interface->text(QAccessible::DebugDescription);
currentLevel.role = translateRole(interface->role());
currentLevel.rect = interface->rect();
currentLevel.state = interface->state();
currentLevel.object = interface->object();
return currentLevel;
}
void AccessibilitySceneManager::populateAccessibilityTreeScene(QAccessibleInterface * interface)
{
if (!interface)
return;
// set some layout metrics:
m_treeItemWidth = 90;
m_treeItemHorizontalPadding = 10;
m_treeItemHeight = 60;
m_treeItemVerticalPadding = 30;
// We want to draw the accessibility hiearchy as a vertical
// tree, growing from the root node at the top.
// First, figure out the number of levels and the width of each level:
m_rootTreeItem = computeLevels(interface, 0);
// create graphics items for each tree item
addGraphicsItems(m_rootTreeItem, 0, 0);
}
void AccessibilitySceneManager::addGraphicsItems(AccessibilitySceneManager::TreeItem item, int row, int xPos)
{
//qDebug() << "add graphics item" << row << item.name << item.role << xPos << item.width << item.children.count();
int yPos = row * (m_treeItemHeight + m_treeItemVerticalPadding);
// Process this interface
QGraphicsRectItem * graphicsItem = new QGraphicsRectItem();
graphicsItem->setPos(xPos, yPos);
graphicsItem->setRect(0, 0, m_treeItemWidth, m_treeItemHeight);
graphicsItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
if (item.object && item.object == m_selectedObject)
graphicsItem->setBrush(QColor(Qt::yellow));
else
graphicsItem->setBrush(QColor(Qt::white));
if (item.state.offscreen) {
QPen linePen;
linePen.setStyle(Qt::DashLine);
graphicsItem->setPen(linePen);
}
m_treeScene->addItem(graphicsItem);
QGraphicsTextItem * textItem = new QGraphicsTextItem();
textItem->setParentItem(graphicsItem);
textItem->setPos(QPoint(0, 0));
QFont font;
font.setPointSize(8);
textItem->setFont(font);
QString text;
text += item.name + "\n";
text += item.role + "\n";
text += item.description.split(QLatin1Char(' '), Qt::SkipEmptyParts).join("\n") + "\n";
text += "P:" + QString::number(item.rect.x()) + " " + QString::number(item.rect.y()) + " ";
text += "S:" + QString::number(item.rect.width()) + " " + QString::number(item.rect.height()) + "\n";
textItem->setPlainText(text);
// recurse to children
int childIndex = 0;
int childCount = item.children.count();
int segmentSize = item.width / qMax(1, childCount);
int segmentCenterOffset = segmentSize / 2;
int segmentsStart = xPos - (item.width / 2);
foreach (TreeItem child, item.children) {
// spread the children out, covering the width, centered on xPos
int segmentPosition = segmentsStart + (segmentSize * childIndex) + segmentCenterOffset;
addGraphicsItems(child, row + 1, segmentPosition);
++childIndex;
}
// add lines from parents to kids
int boxBottom = yPos + m_treeItemHeight;
int boxMiddleX = xPos + m_treeItemWidth / 2;
int yBottomMiddle = boxBottom + m_treeItemVerticalPadding / 2;
int boxTop = yPos;
int yTopMiddle = boxTop - m_treeItemVerticalPadding / 2;
if (row > 0) {
QGraphicsLineItem *childVerticalStem = new QGraphicsLineItem();
childVerticalStem->setLine(boxMiddleX, yTopMiddle, boxMiddleX, boxTop);
m_treeScene->addItem(childVerticalStem);
}
if (childCount > 0) {
QGraphicsLineItem *parentVerticalStem = new QGraphicsLineItem();
parentVerticalStem->setLine(boxMiddleX, boxBottom, boxMiddleX, yBottomMiddle);
m_treeScene->addItem(parentVerticalStem);
}
if (childCount > 1) {
QGraphicsLineItem *horizontalStem = new QGraphicsLineItem();
// match the end points with the horizontal lines
int lineStartX = segmentsStart + segmentCenterOffset + m_treeItemWidth / 2;
int lineStopX = segmentsStart + segmentSize * (childCount -1) + segmentCenterOffset + m_treeItemWidth / 2;
horizontalStem->setLine(lineStartX, yBottomMiddle, lineStopX , yBottomMiddle);
m_treeScene->addItem(horizontalStem);
}
}
bool AccessibilitySceneManager::isHidden(QAccessibleInterface *interface)
{
QAccessibleInterface *current = interface;
while (current) {
if (current->state().invisible) {
return true;
}
current = current->parent();
}
return false;
}

View File

@ -0,0 +1,79 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef ACCESSIBILITYSCENEMANAGER_H
#define ACCESSIBILITYSCENEMANAGER_H
#include <QtGui>
#include "optionswidget.h"
QString translateRole(QAccessible::Role role);
class AccessibilitySceneManager : public QObject
{
Q_OBJECT
public:
AccessibilitySceneManager();
void setRootWindow(QWindow * window) { m_window = window; }
void setView(QGraphicsView *view) { m_view = view; }
void setScene(QGraphicsScene *scene) { m_scene = scene; }
void setTreeView(QGraphicsView *treeView) { m_treeView = treeView; }
void setTreeScene(QGraphicsScene *treeScene) { m_treeScene = treeScene; }
void setOptionsWidget(OptionsWidget *optionsWidget) { m_optionsWidget = optionsWidget; }
public slots:
void populateAccessibilityScene();
void updateAccessibilitySceneItemFlags();
void populateAccessibilityTreeScene();
void handleUpdate(QAccessibleEvent *event);
void setSelected(QObject *object);
void changeScale(int scale);
private:
void updateItems(QObject *root);
void updateItem(QObject *object);
void updateItem(QGraphicsRectItem *item, QAccessibleInterface *interface);
void updateItemFlags(QGraphicsRectItem *item, QAccessibleInterface *interface);
void populateAccessibilityScene(QAccessibleInterface * interface, QGraphicsScene *scene);
QGraphicsRectItem * processInterface(QAccessibleInterface * interface, QGraphicsScene *scene);
struct TreeItem;
TreeItem computeLevels(QAccessibleInterface * interface, int level);
void populateAccessibilityTreeScene(QAccessibleInterface * interface);
void addGraphicsItems(TreeItem item, int row, int xPos);
bool isHidden(QAccessibleInterface *interface);
QWindow *m_window;
QGraphicsView *m_view;
QGraphicsScene *m_scene;
QGraphicsView *m_treeView;
QGraphicsScene *m_treeScene;
QGraphicsItem *m_rootItem;
OptionsWidget *m_optionsWidget;
QObject *m_selectedObject;
QHash<QObject *, QGraphicsRectItem*> m_graphicsItems;
QSet<QObject *> m_animatedObjects;
struct TreeItem {
QList<TreeItem> children;
int width;
QString name;
QString role;
QString description;
QRect rect;
QAccessible::State state;
QObject *object;
TreeItem() : width(0) {}
};
TreeItem m_rootTreeItem;
int m_treeItemWidth;
int m_treeItemHorizontalPadding;
int m_treeItemHeight;
int m_treeItemVerticalPadding;
};
#endif // ACCESSIBILITYSCENEMANAGER_H

View File

@ -0,0 +1,81 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtGui>
#include <QtDeclarative/QtDeclarative>
#include <QtUiTools/QtUiTools>
#include "accessibilityinspector.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
if (app.arguments().count() < 2) {
qDebug() << "Usage: accessebilityInspector [ ui-file | qml-file ] [Option]";
qDebug() << "Option:";
#ifdef QT_ACCESSIBILITY_INSPECTOR_SCENE_GRAPH
qDebug() << "-qtquick1: Use QDeclarativeView instead of QSGView for rendering QML files";
#endif
return 0;
}
QString fileName = app.arguments().at(1);
QString mode;
if (app.arguments().count() > 2) {
mode = app.arguments().at(2);
}
QWidget *window;
if (fileName.endsWith(".ui")) {
QUiLoader loader;
QFile file(fileName);
file.open(QFile::ReadOnly);
window = loader.load(&file, 0);
} else if (fileName.endsWith(".qml")){
QUrl fileUrl;
if (fileName.startsWith(":")) { // detect resources.
QString name = fileName;
name.remove(0, 2); // reomve ":/"
fileUrl.setUrl(QLatin1String("qrc:/") + name);
} else {
fileUrl = QUrl::fromLocalFile(fileName);
}
#ifdef QT_ACCESSIBILITY_INSPECTOR_SCENE_GRAPH
if (mode == QLatin1String("-qtquick1"))
#endif
{
QDeclarativeView * declarativeView = new QDeclarativeView();
declarativeView->setSource(fileUrl);
window = declarativeView;
}
#ifdef QT_ACCESSIBILITY_INSPECTOR_SCENE_GRAPH
else {
QSGView * sceneGraphView = new QSGView();
sceneGraphView->setSource(fileUrl);
window = sceneGraphView;
}
#endif
} else {
qDebug() << "Error: don't know what to do with" << fileName;
}
AccessibilityInspector *accessibilityInspector = new AccessibilityInspector();
accessibilityInspector->inspectWindow(window);
window->move(50, 50);
window->show();
int ret = app.exec();
accessibilityInspector->saveWindowGeometry();
delete accessibilityInspector;
return ret;
}

View File

@ -0,0 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "optionswidget.h"

View File

@ -0,0 +1,95 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef OPTIONSWIDGET_H
#define OPTIONSWIDGET_H
#include <QtGui>
#include <QtWidgets>
class OptionsWidget : public QWidget
{
Q_OBJECT
public:
OptionsWidget()
:QWidget()
{
QVBoxLayout *m_layout = new QVBoxLayout;
m_refresh = new QPushButton(this);
m_refresh->setText(QLatin1String("Refresh"));
m_layout->addWidget(m_refresh);
connect(m_refresh, SIGNAL(clicked()), SIGNAL(refreshClicked()));
m_hideInvisibleItems = new QCheckBox(this);
m_layout->addWidget(m_hideInvisibleItems);
m_hideInvisibleItems->setText("Hide Invisible Items");
m_hideInvisibleItems->setChecked(true);
connect(m_hideInvisibleItems, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
m_hideOffscreenItems = new QCheckBox(this);
m_layout->addWidget(m_hideOffscreenItems);
m_hideOffscreenItems->setText("Hide Offscreen Items");
m_hideOffscreenItems->setChecked(true);
connect(m_hideOffscreenItems, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
m_hidePaneItems = new QCheckBox(this);
m_layout->addWidget(m_hidePaneItems);
m_hidePaneItems->setText("Hide Items with the Pane role");
m_hidePaneItems->setChecked(true);
connect(m_hidePaneItems, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
m_hideNullObjectItems = new QCheckBox(this);
m_layout->addWidget(m_hideNullObjectItems);
m_hideNullObjectItems->setText("Hide Items with a null QObject pointer");
m_hideNullObjectItems->setChecked(true);
connect(m_hideNullObjectItems, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
m_hideNullRectItems = new QCheckBox(this);
m_layout->addWidget(m_hideNullRectItems);
m_hideNullRectItems->setText("Hide Items with a null rect");
m_hideNullRectItems->setChecked(true);
connect(m_hideNullRectItems, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
m_enableTextToSpeach = new QCheckBox(this);
m_layout->addWidget(m_enableTextToSpeach);
m_enableTextToSpeach->setText("Enable Text To Speech");
m_enableTextToSpeach->setChecked(false);
connect(m_enableTextToSpeach, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
m_scale = new QSlider(Qt::Horizontal);
// m_layout->addWidget(m_scale);
m_scale->setRange(5, 30);
m_scale->setValue(1);
connect(m_scale, SIGNAL(valueChanged(int)), SIGNAL(scaleChanged(int)));
this->setLayout(m_layout);
}
bool hideInvisibleItems() { return m_hideInvisibleItems->isChecked(); }
bool hideOffscreenItems() { return m_hideOffscreenItems->isChecked(); }
bool hidePaneItems() { return m_hidePaneItems->isChecked(); }
bool hideNullObjectItems() { return m_hideNullObjectItems->isChecked(); }
bool hideNullRectItems() { return m_hideNullRectItems->isChecked(); }
bool enableTextToSpeach() { return m_enableTextToSpeach->isChecked(); }
signals:
void optionsChanged();
void refreshClicked();
void scaleChanged(int);
private:
QVBoxLayout *m_layout;
QPushButton *m_refresh;
QCheckBox *m_hideInvisibleItems;
QCheckBox *m_hideOffscreenItems;
QCheckBox *m_hidePaneItems;
QCheckBox *m_hideNullObjectItems;
QCheckBox *m_hideNullRectItems;
QCheckBox *m_enableTextToSpeach;
QSlider *m_scale;
};
#endif // OPTIONSWIDGET_H

View File

@ -0,0 +1,99 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "screenreader.h"
#include "optionswidget.h"
#include "accessibilityscenemanager.h"
#include <QtGui>
ScreenReader::ScreenReader(QObject *parent) :
QObject(parent)
{
m_selectedInterface = 0;
m_rootInterface = 0;
bool activateCalled = false;
}
ScreenReader::~ScreenReader()
{
}
void ScreenReader::setRootObject(QObject *rootObject)
{
m_rootInterface = QAccessible::queryAccessibleInterface(rootObject);
}
void ScreenReader::setOptionsWidget(OptionsWidget *optionsWidget)
{
m_optionsWidget = optionsWidget;
}
void ScreenReader::touchPoint(const QPoint &point)
{
qDebug() << "touch" << point;
// Wait and see if this touch is the start of a double-tap
// (activate will then be called and cancel the touch processing)
m_activateCalled = false;
m_currentTouchPoint = point;
QTimer::singleShot(200, this, SLOT(processTouchPoint()));
}
void ScreenReader::processTouchPoint()
{
if (m_activateCalled) {
return;
}
if (m_rootInterface == 0) {
return;
}
QAccessibleInterface * currentInterface = m_rootInterface;
int hit = -2;
int guardCounter = 0;
const int guardMax = 40;
while (currentInterface != 0) {
++guardCounter;
if (guardCounter > guardMax) {
qDebug() << "touchPoint exit recursion overflow";
return; // outside
}
QAccessibleInterface * hit = currentInterface->childAt(m_currentTouchPoint.x(), m_currentTouchPoint.y());
if (!hit)
break;
currentInterface = hit;
}
m_selectedInterface = currentInterface;
if (m_selectedInterface->object())
emit selected(m_selectedInterface->object());
if (m_optionsWidget->enableTextToSpeach())
speak(m_selectedInterface->text(QAccessible::Name)
/*+ "," + translateRole(m_selectedInterface->role(0)) */);
// qDebug() << "touchPoint exit found" << m_selectedInterface->text(QAccessible::Name, 0) << m_selectedInterface->object() << m_selectedInterface->rect(0);
}
void ScreenReader::activate()
{
qDebug() << "ScreenReader::activate";
m_activateCalled = true;
if (m_selectedInterface) {
m_selectedInterface->actionInterface()->doAction(QAccessibleActionInterface::pressAction());
}
}
void ScreenReader::speak(const QString &text, const QString &/*voice*/)
{
QFile f("festivalspeachhack");
f.open(QIODevice::WriteOnly);
f.write(text.toLocal8Bit());
f.close();
QProcess *process = new QProcess;
process->start("/usr/bin/festival", QStringList() << "--tts" << "festivalspeachhack");
}

View File

@ -0,0 +1,46 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef SCREENREADER_H
#define SCREENREADER_H
#include <QObject>
#include <QAccessible>
#include <QAccessibleBridge>
/*
A Simple screen reader for touch-based user interfaces.
Requires a text-to-speach backend. Currently implemented
using festival on unix.
*/
class OptionsWidget;
class ScreenReader : public QObject
{
Q_OBJECT
public:
explicit ScreenReader(QObject *parent = nullptr);
~ScreenReader();
void setRootObject(QObject *rootObject);
void setOptionsWidget(OptionsWidget *optionsWidget);
public slots:
void touchPoint(const QPoint &point);
void activate();
protected slots:
void processTouchPoint();
signals:
void selected(QObject *object);
protected:
void speak(const QString &text, const QString &voice = QString());
private:
QAccessibleInterface *m_selectedInterface;
QAccessibleInterface *m_rootInterface;
OptionsWidget *m_optionsWidget;
QPoint m_currentTouchPoint;
bool m_activateCalled;
};
#endif // SCREENREADER_H

1
util/aglfn/README Normal file
View File

@ -0,0 +1 @@
Aglfn is used to generate the Adobe Glyph List For New Fonts data in src/gui/text.

3
util/aglfn/aglfn.pro Normal file
View File

@ -0,0 +1,3 @@
SOURCES += main.cpp
QT = core
CONFIG += console

697
util/aglfn/data/aglfn.txt Normal file
View File

@ -0,0 +1,697 @@
# -----------------------------------------------------------
# Copyright 2003, 2005-2008, 2010 Adobe Systems Incorporated.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the
# following conditions are met:
#
# Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# Neither the name of Adobe Systems Incorporated nor the names
# of its contributors may be used to endorse or promote
# products derived from this software without specific prior
# written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------
# Name: Adobe Glyph List For New Fonts
# Table version: 1.7
# Date: November 6, 2008
# URL: http://sourceforge.net/adobe/aglfn/
#
# Description:
#
# AGLFN (Adobe Glyph List For New Fonts) provides a list of base glyph
# names that are recommended for new fonts, which are compatible with
# the AGL (Adobe Glyph List) Specification, and which should be used
# as described in Section 6 of that document. AGLFN comprises the set
# of glyph names from AGL that map via the AGL Specification rules to
# the semantically correct UV (Unicode Value). For example, "Asmall"
# is omitted because AGL maps this glyph name to the PUA (Private Use
# Area) value U+F761, rather than to the UV that maps from the glyph
# name "A." Also omitted is "ffi," because AGL maps this to the
# Alphabetic Presentation Forms value U+FB03, rather than decomposing
# it into the following sequence of three UVs: U+0066, U+0066, and
# U+0069. The name "arrowvertex" has been omitted because this glyph
# now has a real UV, and AGL is now incorrect in mapping it to the PUA
# value U+F8E6. If you do not find an appropriate name for your glyph
# in this list, then please refer to Section 6 of the AGL
# Specification.
#
# Format: three semicolon-delimited fields:
# (1) Standard UV or CUS UV--four uppercase hexadecimal digits
# (2) Glyph name--upper/lowercase letters and digits
# (3) Character names: Unicode character names for standard UVs, and
# descriptive names for CUS UVs--uppercase letters, hyphen, and
# space
#
# The records are sorted by glyph name in increasing ASCII order,
# entries with the same glyph name are sorted in decreasing priority
# order, the UVs and Unicode character names are provided for
# convenience, lines starting with "#" are comments, and blank lines
# should be ignored.
#
# Revision History:
#
# 1.7 [6 November 2008]
# - Reverted to the original 1.4 and earlier mappings for Delta,
# Omega, and mu.
# - Removed mappings for "afii" names. These should now be assigned
# "uni" names.
# - Removed mappings for "commaaccent" names. These should now be
# assigned "uni" names.
#
# 1.6 [30 January 2006]
# - Completed work intended in 1.5.
#
# 1.5 [23 November 2005]
# - Removed duplicated block at end of file.
# - Changed mappings:
# 2206;Delta;INCREMENT changed to 0394;Delta;GREEK CAPITAL LETTER DELTA
# 2126;Omega;OHM SIGN changed to 03A9;Omega;GREEK CAPITAL LETTER OMEGA
# 03BC;mu;MICRO SIGN changed to 03BC;mu;GREEK SMALL LETTER MU
# - Corrected statement above about why "ffi" is omitted.
#
# 1.4 [24 September 2003]
# - Changed version to 1.4, to avoid confusion with the AGL 1.3.
# - Fixed spelling errors in the header.
# - Fully removed "arrowvertex," as it is mapped only to a PUA Unicode
# value in some fonts.
#
# 1.1 [17 April 2003]
# - Renamed [Tt]cedilla back to [Tt]commaaccent.
#
# 1.0 [31 January 2003]
# - Original version.
# - Derived from the AGLv1.2 by:
# removing the PUA area codes;
# removing duplicate Unicode mappings; and
# renaming "tcommaaccent" to "tcedilla" and "Tcommaaccent" to "Tcedilla"
#
0041;A;LATIN CAPITAL LETTER A
00C6;AE;LATIN CAPITAL LETTER AE
01FC;AEacute;LATIN CAPITAL LETTER AE WITH ACUTE
00C1;Aacute;LATIN CAPITAL LETTER A WITH ACUTE
0102;Abreve;LATIN CAPITAL LETTER A WITH BREVE
00C2;Acircumflex;LATIN CAPITAL LETTER A WITH CIRCUMFLEX
00C4;Adieresis;LATIN CAPITAL LETTER A WITH DIAERESIS
00C0;Agrave;LATIN CAPITAL LETTER A WITH GRAVE
0391;Alpha;GREEK CAPITAL LETTER ALPHA
0386;Alphatonos;GREEK CAPITAL LETTER ALPHA WITH TONOS
0100;Amacron;LATIN CAPITAL LETTER A WITH MACRON
0104;Aogonek;LATIN CAPITAL LETTER A WITH OGONEK
00C5;Aring;LATIN CAPITAL LETTER A WITH RING ABOVE
01FA;Aringacute;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
00C3;Atilde;LATIN CAPITAL LETTER A WITH TILDE
0042;B;LATIN CAPITAL LETTER B
0392;Beta;GREEK CAPITAL LETTER BETA
0043;C;LATIN CAPITAL LETTER C
0106;Cacute;LATIN CAPITAL LETTER C WITH ACUTE
010C;Ccaron;LATIN CAPITAL LETTER C WITH CARON
00C7;Ccedilla;LATIN CAPITAL LETTER C WITH CEDILLA
0108;Ccircumflex;LATIN CAPITAL LETTER C WITH CIRCUMFLEX
010A;Cdotaccent;LATIN CAPITAL LETTER C WITH DOT ABOVE
03A7;Chi;GREEK CAPITAL LETTER CHI
0044;D;LATIN CAPITAL LETTER D
010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON
0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE
2206;Delta;INCREMENT
0045;E;LATIN CAPITAL LETTER E
00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE
0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE
011A;Ecaron;LATIN CAPITAL LETTER E WITH CARON
00CA;Ecircumflex;LATIN CAPITAL LETTER E WITH CIRCUMFLEX
00CB;Edieresis;LATIN CAPITAL LETTER E WITH DIAERESIS
0116;Edotaccent;LATIN CAPITAL LETTER E WITH DOT ABOVE
00C8;Egrave;LATIN CAPITAL LETTER E WITH GRAVE
0112;Emacron;LATIN CAPITAL LETTER E WITH MACRON
014A;Eng;LATIN CAPITAL LETTER ENG
0118;Eogonek;LATIN CAPITAL LETTER E WITH OGONEK
0395;Epsilon;GREEK CAPITAL LETTER EPSILON
0388;Epsilontonos;GREEK CAPITAL LETTER EPSILON WITH TONOS
0397;Eta;GREEK CAPITAL LETTER ETA
0389;Etatonos;GREEK CAPITAL LETTER ETA WITH TONOS
00D0;Eth;LATIN CAPITAL LETTER ETH
20AC;Euro;EURO SIGN
0046;F;LATIN CAPITAL LETTER F
0047;G;LATIN CAPITAL LETTER G
0393;Gamma;GREEK CAPITAL LETTER GAMMA
011E;Gbreve;LATIN CAPITAL LETTER G WITH BREVE
01E6;Gcaron;LATIN CAPITAL LETTER G WITH CARON
011C;Gcircumflex;LATIN CAPITAL LETTER G WITH CIRCUMFLEX
0120;Gdotaccent;LATIN CAPITAL LETTER G WITH DOT ABOVE
0048;H;LATIN CAPITAL LETTER H
25CF;H18533;BLACK CIRCLE
25AA;H18543;BLACK SMALL SQUARE
25AB;H18551;WHITE SMALL SQUARE
25A1;H22073;WHITE SQUARE
0126;Hbar;LATIN CAPITAL LETTER H WITH STROKE
0124;Hcircumflex;LATIN CAPITAL LETTER H WITH CIRCUMFLEX
0049;I;LATIN CAPITAL LETTER I
0132;IJ;LATIN CAPITAL LIGATURE IJ
00CD;Iacute;LATIN CAPITAL LETTER I WITH ACUTE
012C;Ibreve;LATIN CAPITAL LETTER I WITH BREVE
00CE;Icircumflex;LATIN CAPITAL LETTER I WITH CIRCUMFLEX
00CF;Idieresis;LATIN CAPITAL LETTER I WITH DIAERESIS
0130;Idotaccent;LATIN CAPITAL LETTER I WITH DOT ABOVE
2111;Ifraktur;BLACK-LETTER CAPITAL I
00CC;Igrave;LATIN CAPITAL LETTER I WITH GRAVE
012A;Imacron;LATIN CAPITAL LETTER I WITH MACRON
012E;Iogonek;LATIN CAPITAL LETTER I WITH OGONEK
0399;Iota;GREEK CAPITAL LETTER IOTA
03AA;Iotadieresis;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
038A;Iotatonos;GREEK CAPITAL LETTER IOTA WITH TONOS
0128;Itilde;LATIN CAPITAL LETTER I WITH TILDE
004A;J;LATIN CAPITAL LETTER J
0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX
004B;K;LATIN CAPITAL LETTER K
039A;Kappa;GREEK CAPITAL LETTER KAPPA
004C;L;LATIN CAPITAL LETTER L
0139;Lacute;LATIN CAPITAL LETTER L WITH ACUTE
039B;Lambda;GREEK CAPITAL LETTER LAMDA
013D;Lcaron;LATIN CAPITAL LETTER L WITH CARON
013F;Ldot;LATIN CAPITAL LETTER L WITH MIDDLE DOT
0141;Lslash;LATIN CAPITAL LETTER L WITH STROKE
004D;M;LATIN CAPITAL LETTER M
039C;Mu;GREEK CAPITAL LETTER MU
004E;N;LATIN CAPITAL LETTER N
0143;Nacute;LATIN CAPITAL LETTER N WITH ACUTE
0147;Ncaron;LATIN CAPITAL LETTER N WITH CARON
00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE
039D;Nu;GREEK CAPITAL LETTER NU
004F;O;LATIN CAPITAL LETTER O
0152;OE;LATIN CAPITAL LIGATURE OE
00D3;Oacute;LATIN CAPITAL LETTER O WITH ACUTE
014E;Obreve;LATIN CAPITAL LETTER O WITH BREVE
00D4;Ocircumflex;LATIN CAPITAL LETTER O WITH CIRCUMFLEX
00D6;Odieresis;LATIN CAPITAL LETTER O WITH DIAERESIS
00D2;Ograve;LATIN CAPITAL LETTER O WITH GRAVE
01A0;Ohorn;LATIN CAPITAL LETTER O WITH HORN
0150;Ohungarumlaut;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
014C;Omacron;LATIN CAPITAL LETTER O WITH MACRON
2126;Omega;OHM SIGN
038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS
039F;Omicron;GREEK CAPITAL LETTER OMICRON
038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS
00D8;Oslash;LATIN CAPITAL LETTER O WITH STROKE
01FE;Oslashacute;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
00D5;Otilde;LATIN CAPITAL LETTER O WITH TILDE
0050;P;LATIN CAPITAL LETTER P
03A6;Phi;GREEK CAPITAL LETTER PHI
03A0;Pi;GREEK CAPITAL LETTER PI
03A8;Psi;GREEK CAPITAL LETTER PSI
0051;Q;LATIN CAPITAL LETTER Q
0052;R;LATIN CAPITAL LETTER R
0154;Racute;LATIN CAPITAL LETTER R WITH ACUTE
0158;Rcaron;LATIN CAPITAL LETTER R WITH CARON
211C;Rfraktur;BLACK-LETTER CAPITAL R
03A1;Rho;GREEK CAPITAL LETTER RHO
0053;S;LATIN CAPITAL LETTER S
250C;SF010000;BOX DRAWINGS LIGHT DOWN AND RIGHT
2514;SF020000;BOX DRAWINGS LIGHT UP AND RIGHT
2510;SF030000;BOX DRAWINGS LIGHT DOWN AND LEFT
2518;SF040000;BOX DRAWINGS LIGHT UP AND LEFT
253C;SF050000;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
252C;SF060000;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
2534;SF070000;BOX DRAWINGS LIGHT UP AND HORIZONTAL
251C;SF080000;BOX DRAWINGS LIGHT VERTICAL AND RIGHT
2524;SF090000;BOX DRAWINGS LIGHT VERTICAL AND LEFT
2500;SF100000;BOX DRAWINGS LIGHT HORIZONTAL
2502;SF110000;BOX DRAWINGS LIGHT VERTICAL
2561;SF190000;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
2562;SF200000;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
2556;SF210000;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
2555;SF220000;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
2563;SF230000;BOX DRAWINGS DOUBLE VERTICAL AND LEFT
2551;SF240000;BOX DRAWINGS DOUBLE VERTICAL
2557;SF250000;BOX DRAWINGS DOUBLE DOWN AND LEFT
255D;SF260000;BOX DRAWINGS DOUBLE UP AND LEFT
255C;SF270000;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
255B;SF280000;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
255E;SF360000;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
255F;SF370000;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
255A;SF380000;BOX DRAWINGS DOUBLE UP AND RIGHT
2554;SF390000;BOX DRAWINGS DOUBLE DOWN AND RIGHT
2569;SF400000;BOX DRAWINGS DOUBLE UP AND HORIZONTAL
2566;SF410000;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
2560;SF420000;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
2550;SF430000;BOX DRAWINGS DOUBLE HORIZONTAL
256C;SF440000;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
2567;SF450000;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
2568;SF460000;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
2564;SF470000;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
2565;SF480000;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
2559;SF490000;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
2558;SF500000;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
2552;SF510000;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
2553;SF520000;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
256B;SF530000;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
256A;SF540000;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
015A;Sacute;LATIN CAPITAL LETTER S WITH ACUTE
0160;Scaron;LATIN CAPITAL LETTER S WITH CARON
015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA
015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX
03A3;Sigma;GREEK CAPITAL LETTER SIGMA
0054;T;LATIN CAPITAL LETTER T
03A4;Tau;GREEK CAPITAL LETTER TAU
0166;Tbar;LATIN CAPITAL LETTER T WITH STROKE
0164;Tcaron;LATIN CAPITAL LETTER T WITH CARON
0398;Theta;GREEK CAPITAL LETTER THETA
00DE;Thorn;LATIN CAPITAL LETTER THORN
0055;U;LATIN CAPITAL LETTER U
00DA;Uacute;LATIN CAPITAL LETTER U WITH ACUTE
016C;Ubreve;LATIN CAPITAL LETTER U WITH BREVE
00DB;Ucircumflex;LATIN CAPITAL LETTER U WITH CIRCUMFLEX
00DC;Udieresis;LATIN CAPITAL LETTER U WITH DIAERESIS
00D9;Ugrave;LATIN CAPITAL LETTER U WITH GRAVE
01AF;Uhorn;LATIN CAPITAL LETTER U WITH HORN
0170;Uhungarumlaut;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
016A;Umacron;LATIN CAPITAL LETTER U WITH MACRON
0172;Uogonek;LATIN CAPITAL LETTER U WITH OGONEK
03A5;Upsilon;GREEK CAPITAL LETTER UPSILON
03D2;Upsilon1;GREEK UPSILON WITH HOOK SYMBOL
03AB;Upsilondieresis;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
038E;Upsilontonos;GREEK CAPITAL LETTER UPSILON WITH TONOS
016E;Uring;LATIN CAPITAL LETTER U WITH RING ABOVE
0168;Utilde;LATIN CAPITAL LETTER U WITH TILDE
0056;V;LATIN CAPITAL LETTER V
0057;W;LATIN CAPITAL LETTER W
1E82;Wacute;LATIN CAPITAL LETTER W WITH ACUTE
0174;Wcircumflex;LATIN CAPITAL LETTER W WITH CIRCUMFLEX
1E84;Wdieresis;LATIN CAPITAL LETTER W WITH DIAERESIS
1E80;Wgrave;LATIN CAPITAL LETTER W WITH GRAVE
0058;X;LATIN CAPITAL LETTER X
039E;Xi;GREEK CAPITAL LETTER XI
0059;Y;LATIN CAPITAL LETTER Y
00DD;Yacute;LATIN CAPITAL LETTER Y WITH ACUTE
0176;Ycircumflex;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
0178;Ydieresis;LATIN CAPITAL LETTER Y WITH DIAERESIS
1EF2;Ygrave;LATIN CAPITAL LETTER Y WITH GRAVE
005A;Z;LATIN CAPITAL LETTER Z
0179;Zacute;LATIN CAPITAL LETTER Z WITH ACUTE
017D;Zcaron;LATIN CAPITAL LETTER Z WITH CARON
017B;Zdotaccent;LATIN CAPITAL LETTER Z WITH DOT ABOVE
0396;Zeta;GREEK CAPITAL LETTER ZETA
0061;a;LATIN SMALL LETTER A
00E1;aacute;LATIN SMALL LETTER A WITH ACUTE
0103;abreve;LATIN SMALL LETTER A WITH BREVE
00E2;acircumflex;LATIN SMALL LETTER A WITH CIRCUMFLEX
00B4;acute;ACUTE ACCENT
0301;acutecomb;COMBINING ACUTE ACCENT
00E4;adieresis;LATIN SMALL LETTER A WITH DIAERESIS
00E6;ae;LATIN SMALL LETTER AE
01FD;aeacute;LATIN SMALL LETTER AE WITH ACUTE
00E0;agrave;LATIN SMALL LETTER A WITH GRAVE
2135;aleph;ALEF SYMBOL
03B1;alpha;GREEK SMALL LETTER ALPHA
03AC;alphatonos;GREEK SMALL LETTER ALPHA WITH TONOS
0101;amacron;LATIN SMALL LETTER A WITH MACRON
0026;ampersand;AMPERSAND
2220;angle;ANGLE
2329;angleleft;LEFT-POINTING ANGLE BRACKET
232A;angleright;RIGHT-POINTING ANGLE BRACKET
0387;anoteleia;GREEK ANO TELEIA
0105;aogonek;LATIN SMALL LETTER A WITH OGONEK
2248;approxequal;ALMOST EQUAL TO
00E5;aring;LATIN SMALL LETTER A WITH RING ABOVE
01FB;aringacute;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
2194;arrowboth;LEFT RIGHT ARROW
21D4;arrowdblboth;LEFT RIGHT DOUBLE ARROW
21D3;arrowdbldown;DOWNWARDS DOUBLE ARROW
21D0;arrowdblleft;LEFTWARDS DOUBLE ARROW
21D2;arrowdblright;RIGHTWARDS DOUBLE ARROW
21D1;arrowdblup;UPWARDS DOUBLE ARROW
2193;arrowdown;DOWNWARDS ARROW
2190;arrowleft;LEFTWARDS ARROW
2192;arrowright;RIGHTWARDS ARROW
2191;arrowup;UPWARDS ARROW
2195;arrowupdn;UP DOWN ARROW
21A8;arrowupdnbse;UP DOWN ARROW WITH BASE
005E;asciicircum;CIRCUMFLEX ACCENT
007E;asciitilde;TILDE
002A;asterisk;ASTERISK
2217;asteriskmath;ASTERISK OPERATOR
0040;at;COMMERCIAL AT
00E3;atilde;LATIN SMALL LETTER A WITH TILDE
0062;b;LATIN SMALL LETTER B
005C;backslash;REVERSE SOLIDUS
007C;bar;VERTICAL LINE
03B2;beta;GREEK SMALL LETTER BETA
2588;block;FULL BLOCK
007B;braceleft;LEFT CURLY BRACKET
007D;braceright;RIGHT CURLY BRACKET
005B;bracketleft;LEFT SQUARE BRACKET
005D;bracketright;RIGHT SQUARE BRACKET
02D8;breve;BREVE
00A6;brokenbar;BROKEN BAR
2022;bullet;BULLET
0063;c;LATIN SMALL LETTER C
0107;cacute;LATIN SMALL LETTER C WITH ACUTE
02C7;caron;CARON
21B5;carriagereturn;DOWNWARDS ARROW WITH CORNER LEFTWARDS
010D;ccaron;LATIN SMALL LETTER C WITH CARON
00E7;ccedilla;LATIN SMALL LETTER C WITH CEDILLA
0109;ccircumflex;LATIN SMALL LETTER C WITH CIRCUMFLEX
010B;cdotaccent;LATIN SMALL LETTER C WITH DOT ABOVE
00B8;cedilla;CEDILLA
00A2;cent;CENT SIGN
03C7;chi;GREEK SMALL LETTER CHI
25CB;circle;WHITE CIRCLE
2297;circlemultiply;CIRCLED TIMES
2295;circleplus;CIRCLED PLUS
02C6;circumflex;MODIFIER LETTER CIRCUMFLEX ACCENT
2663;club;BLACK CLUB SUIT
003A;colon;COLON
20A1;colonmonetary;COLON SIGN
002C;comma;COMMA
2245;congruent;APPROXIMATELY EQUAL TO
00A9;copyright;COPYRIGHT SIGN
00A4;currency;CURRENCY SIGN
0064;d;LATIN SMALL LETTER D
2020;dagger;DAGGER
2021;daggerdbl;DOUBLE DAGGER
010F;dcaron;LATIN SMALL LETTER D WITH CARON
0111;dcroat;LATIN SMALL LETTER D WITH STROKE
00B0;degree;DEGREE SIGN
03B4;delta;GREEK SMALL LETTER DELTA
2666;diamond;BLACK DIAMOND SUIT
00A8;dieresis;DIAERESIS
0385;dieresistonos;GREEK DIALYTIKA TONOS
00F7;divide;DIVISION SIGN
2593;dkshade;DARK SHADE
2584;dnblock;LOWER HALF BLOCK
0024;dollar;DOLLAR SIGN
20AB;dong;DONG SIGN
02D9;dotaccent;DOT ABOVE
0323;dotbelowcomb;COMBINING DOT BELOW
0131;dotlessi;LATIN SMALL LETTER DOTLESS I
22C5;dotmath;DOT OPERATOR
0065;e;LATIN SMALL LETTER E
00E9;eacute;LATIN SMALL LETTER E WITH ACUTE
0115;ebreve;LATIN SMALL LETTER E WITH BREVE
011B;ecaron;LATIN SMALL LETTER E WITH CARON
00EA;ecircumflex;LATIN SMALL LETTER E WITH CIRCUMFLEX
00EB;edieresis;LATIN SMALL LETTER E WITH DIAERESIS
0117;edotaccent;LATIN SMALL LETTER E WITH DOT ABOVE
00E8;egrave;LATIN SMALL LETTER E WITH GRAVE
0038;eight;DIGIT EIGHT
2208;element;ELEMENT OF
2026;ellipsis;HORIZONTAL ELLIPSIS
0113;emacron;LATIN SMALL LETTER E WITH MACRON
2014;emdash;EM DASH
2205;emptyset;EMPTY SET
2013;endash;EN DASH
014B;eng;LATIN SMALL LETTER ENG
0119;eogonek;LATIN SMALL LETTER E WITH OGONEK
03B5;epsilon;GREEK SMALL LETTER EPSILON
03AD;epsilontonos;GREEK SMALL LETTER EPSILON WITH TONOS
003D;equal;EQUALS SIGN
2261;equivalence;IDENTICAL TO
212E;estimated;ESTIMATED SYMBOL
03B7;eta;GREEK SMALL LETTER ETA
03AE;etatonos;GREEK SMALL LETTER ETA WITH TONOS
00F0;eth;LATIN SMALL LETTER ETH
0021;exclam;EXCLAMATION MARK
203C;exclamdbl;DOUBLE EXCLAMATION MARK
00A1;exclamdown;INVERTED EXCLAMATION MARK
2203;existential;THERE EXISTS
0066;f;LATIN SMALL LETTER F
2640;female;FEMALE SIGN
2012;figuredash;FIGURE DASH
25A0;filledbox;BLACK SQUARE
25AC;filledrect;BLACK RECTANGLE
0035;five;DIGIT FIVE
215D;fiveeighths;VULGAR FRACTION FIVE EIGHTHS
0192;florin;LATIN SMALL LETTER F WITH HOOK
0034;four;DIGIT FOUR
2044;fraction;FRACTION SLASH
20A3;franc;FRENCH FRANC SIGN
0067;g;LATIN SMALL LETTER G
03B3;gamma;GREEK SMALL LETTER GAMMA
011F;gbreve;LATIN SMALL LETTER G WITH BREVE
01E7;gcaron;LATIN SMALL LETTER G WITH CARON
011D;gcircumflex;LATIN SMALL LETTER G WITH CIRCUMFLEX
0121;gdotaccent;LATIN SMALL LETTER G WITH DOT ABOVE
00DF;germandbls;LATIN SMALL LETTER SHARP S
2207;gradient;NABLA
0060;grave;GRAVE ACCENT
0300;gravecomb;COMBINING GRAVE ACCENT
003E;greater;GREATER-THAN SIGN
2265;greaterequal;GREATER-THAN OR EQUAL TO
00AB;guillemotleft;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
00BB;guillemotright;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
2039;guilsinglleft;SINGLE LEFT-POINTING ANGLE QUOTATION MARK
203A;guilsinglright;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0068;h;LATIN SMALL LETTER H
0127;hbar;LATIN SMALL LETTER H WITH STROKE
0125;hcircumflex;LATIN SMALL LETTER H WITH CIRCUMFLEX
2665;heart;BLACK HEART SUIT
0309;hookabovecomb;COMBINING HOOK ABOVE
2302;house;HOUSE
02DD;hungarumlaut;DOUBLE ACUTE ACCENT
002D;hyphen;HYPHEN-MINUS
0069;i;LATIN SMALL LETTER I
00ED;iacute;LATIN SMALL LETTER I WITH ACUTE
012D;ibreve;LATIN SMALL LETTER I WITH BREVE
00EE;icircumflex;LATIN SMALL LETTER I WITH CIRCUMFLEX
00EF;idieresis;LATIN SMALL LETTER I WITH DIAERESIS
00EC;igrave;LATIN SMALL LETTER I WITH GRAVE
0133;ij;LATIN SMALL LIGATURE IJ
012B;imacron;LATIN SMALL LETTER I WITH MACRON
221E;infinity;INFINITY
222B;integral;INTEGRAL
2321;integralbt;BOTTOM HALF INTEGRAL
2320;integraltp;TOP HALF INTEGRAL
2229;intersection;INTERSECTION
25D8;invbullet;INVERSE BULLET
25D9;invcircle;INVERSE WHITE CIRCLE
263B;invsmileface;BLACK SMILING FACE
012F;iogonek;LATIN SMALL LETTER I WITH OGONEK
03B9;iota;GREEK SMALL LETTER IOTA
03CA;iotadieresis;GREEK SMALL LETTER IOTA WITH DIALYTIKA
0390;iotadieresistonos;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
03AF;iotatonos;GREEK SMALL LETTER IOTA WITH TONOS
0129;itilde;LATIN SMALL LETTER I WITH TILDE
006A;j;LATIN SMALL LETTER J
0135;jcircumflex;LATIN SMALL LETTER J WITH CIRCUMFLEX
006B;k;LATIN SMALL LETTER K
03BA;kappa;GREEK SMALL LETTER KAPPA
0138;kgreenlandic;LATIN SMALL LETTER KRA
006C;l;LATIN SMALL LETTER L
013A;lacute;LATIN SMALL LETTER L WITH ACUTE
03BB;lambda;GREEK SMALL LETTER LAMDA
013E;lcaron;LATIN SMALL LETTER L WITH CARON
0140;ldot;LATIN SMALL LETTER L WITH MIDDLE DOT
003C;less;LESS-THAN SIGN
2264;lessequal;LESS-THAN OR EQUAL TO
258C;lfblock;LEFT HALF BLOCK
20A4;lira;LIRA SIGN
2227;logicaland;LOGICAL AND
00AC;logicalnot;NOT SIGN
2228;logicalor;LOGICAL OR
017F;longs;LATIN SMALL LETTER LONG S
25CA;lozenge;LOZENGE
0142;lslash;LATIN SMALL LETTER L WITH STROKE
2591;ltshade;LIGHT SHADE
006D;m;LATIN SMALL LETTER M
00AF;macron;MACRON
2642;male;MALE SIGN
2212;minus;MINUS SIGN
2032;minute;PRIME
00B5;mu;MICRO SIGN
00D7;multiply;MULTIPLICATION SIGN
266A;musicalnote;EIGHTH NOTE
266B;musicalnotedbl;BEAMED EIGHTH NOTES
006E;n;LATIN SMALL LETTER N
0144;nacute;LATIN SMALL LETTER N WITH ACUTE
0149;napostrophe;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
0148;ncaron;LATIN SMALL LETTER N WITH CARON
0039;nine;DIGIT NINE
2209;notelement;NOT AN ELEMENT OF
2260;notequal;NOT EQUAL TO
2284;notsubset;NOT A SUBSET OF
00F1;ntilde;LATIN SMALL LETTER N WITH TILDE
03BD;nu;GREEK SMALL LETTER NU
0023;numbersign;NUMBER SIGN
006F;o;LATIN SMALL LETTER O
00F3;oacute;LATIN SMALL LETTER O WITH ACUTE
014F;obreve;LATIN SMALL LETTER O WITH BREVE
00F4;ocircumflex;LATIN SMALL LETTER O WITH CIRCUMFLEX
00F6;odieresis;LATIN SMALL LETTER O WITH DIAERESIS
0153;oe;LATIN SMALL LIGATURE OE
02DB;ogonek;OGONEK
00F2;ograve;LATIN SMALL LETTER O WITH GRAVE
01A1;ohorn;LATIN SMALL LETTER O WITH HORN
0151;ohungarumlaut;LATIN SMALL LETTER O WITH DOUBLE ACUTE
014D;omacron;LATIN SMALL LETTER O WITH MACRON
03C9;omega;GREEK SMALL LETTER OMEGA
03D6;omega1;GREEK PI SYMBOL
03CE;omegatonos;GREEK SMALL LETTER OMEGA WITH TONOS
03BF;omicron;GREEK SMALL LETTER OMICRON
03CC;omicrontonos;GREEK SMALL LETTER OMICRON WITH TONOS
0031;one;DIGIT ONE
2024;onedotenleader;ONE DOT LEADER
215B;oneeighth;VULGAR FRACTION ONE EIGHTH
00BD;onehalf;VULGAR FRACTION ONE HALF
00BC;onequarter;VULGAR FRACTION ONE QUARTER
2153;onethird;VULGAR FRACTION ONE THIRD
25E6;openbullet;WHITE BULLET
00AA;ordfeminine;FEMININE ORDINAL INDICATOR
00BA;ordmasculine;MASCULINE ORDINAL INDICATOR
221F;orthogonal;RIGHT ANGLE
00F8;oslash;LATIN SMALL LETTER O WITH STROKE
01FF;oslashacute;LATIN SMALL LETTER O WITH STROKE AND ACUTE
00F5;otilde;LATIN SMALL LETTER O WITH TILDE
0070;p;LATIN SMALL LETTER P
00B6;paragraph;PILCROW SIGN
0028;parenleft;LEFT PARENTHESIS
0029;parenright;RIGHT PARENTHESIS
2202;partialdiff;PARTIAL DIFFERENTIAL
0025;percent;PERCENT SIGN
002E;period;FULL STOP
00B7;periodcentered;MIDDLE DOT
22A5;perpendicular;UP TACK
2030;perthousand;PER MILLE SIGN
20A7;peseta;PESETA SIGN
03C6;phi;GREEK SMALL LETTER PHI
03D5;phi1;GREEK PHI SYMBOL
03C0;pi;GREEK SMALL LETTER PI
002B;plus;PLUS SIGN
00B1;plusminus;PLUS-MINUS SIGN
211E;prescription;PRESCRIPTION TAKE
220F;product;N-ARY PRODUCT
2282;propersubset;SUBSET OF
2283;propersuperset;SUPERSET OF
221D;proportional;PROPORTIONAL TO
03C8;psi;GREEK SMALL LETTER PSI
0071;q;LATIN SMALL LETTER Q
003F;question;QUESTION MARK
00BF;questiondown;INVERTED QUESTION MARK
0022;quotedbl;QUOTATION MARK
201E;quotedblbase;DOUBLE LOW-9 QUOTATION MARK
201C;quotedblleft;LEFT DOUBLE QUOTATION MARK
201D;quotedblright;RIGHT DOUBLE QUOTATION MARK
2018;quoteleft;LEFT SINGLE QUOTATION MARK
201B;quotereversed;SINGLE HIGH-REVERSED-9 QUOTATION MARK
2019;quoteright;RIGHT SINGLE QUOTATION MARK
201A;quotesinglbase;SINGLE LOW-9 QUOTATION MARK
0027;quotesingle;APOSTROPHE
0072;r;LATIN SMALL LETTER R
0155;racute;LATIN SMALL LETTER R WITH ACUTE
221A;radical;SQUARE ROOT
0159;rcaron;LATIN SMALL LETTER R WITH CARON
2286;reflexsubset;SUBSET OF OR EQUAL TO
2287;reflexsuperset;SUPERSET OF OR EQUAL TO
00AE;registered;REGISTERED SIGN
2310;revlogicalnot;REVERSED NOT SIGN
03C1;rho;GREEK SMALL LETTER RHO
02DA;ring;RING ABOVE
2590;rtblock;RIGHT HALF BLOCK
0073;s;LATIN SMALL LETTER S
015B;sacute;LATIN SMALL LETTER S WITH ACUTE
0161;scaron;LATIN SMALL LETTER S WITH CARON
015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA
015D;scircumflex;LATIN SMALL LETTER S WITH CIRCUMFLEX
2033;second;DOUBLE PRIME
00A7;section;SECTION SIGN
003B;semicolon;SEMICOLON
0037;seven;DIGIT SEVEN
215E;seveneighths;VULGAR FRACTION SEVEN EIGHTHS
2592;shade;MEDIUM SHADE
03C3;sigma;GREEK SMALL LETTER SIGMA
03C2;sigma1;GREEK SMALL LETTER FINAL SIGMA
223C;similar;TILDE OPERATOR
0036;six;DIGIT SIX
002F;slash;SOLIDUS
263A;smileface;WHITE SMILING FACE
0020;space;SPACE
2660;spade;BLACK SPADE SUIT
00A3;sterling;POUND SIGN
220B;suchthat;CONTAINS AS MEMBER
2211;summation;N-ARY SUMMATION
263C;sun;WHITE SUN WITH RAYS
0074;t;LATIN SMALL LETTER T
03C4;tau;GREEK SMALL LETTER TAU
0167;tbar;LATIN SMALL LETTER T WITH STROKE
0165;tcaron;LATIN SMALL LETTER T WITH CARON
2234;therefore;THEREFORE
03B8;theta;GREEK SMALL LETTER THETA
03D1;theta1;GREEK THETA SYMBOL
00FE;thorn;LATIN SMALL LETTER THORN
0033;three;DIGIT THREE
215C;threeeighths;VULGAR FRACTION THREE EIGHTHS
00BE;threequarters;VULGAR FRACTION THREE QUARTERS
02DC;tilde;SMALL TILDE
0303;tildecomb;COMBINING TILDE
0384;tonos;GREEK TONOS
2122;trademark;TRADE MARK SIGN
25BC;triagdn;BLACK DOWN-POINTING TRIANGLE
25C4;triaglf;BLACK LEFT-POINTING POINTER
25BA;triagrt;BLACK RIGHT-POINTING POINTER
25B2;triagup;BLACK UP-POINTING TRIANGLE
0032;two;DIGIT TWO
2025;twodotenleader;TWO DOT LEADER
2154;twothirds;VULGAR FRACTION TWO THIRDS
0075;u;LATIN SMALL LETTER U
00FA;uacute;LATIN SMALL LETTER U WITH ACUTE
016D;ubreve;LATIN SMALL LETTER U WITH BREVE
00FB;ucircumflex;LATIN SMALL LETTER U WITH CIRCUMFLEX
00FC;udieresis;LATIN SMALL LETTER U WITH DIAERESIS
00F9;ugrave;LATIN SMALL LETTER U WITH GRAVE
01B0;uhorn;LATIN SMALL LETTER U WITH HORN
0171;uhungarumlaut;LATIN SMALL LETTER U WITH DOUBLE ACUTE
016B;umacron;LATIN SMALL LETTER U WITH MACRON
005F;underscore;LOW LINE
2017;underscoredbl;DOUBLE LOW LINE
222A;union;UNION
2200;universal;FOR ALL
0173;uogonek;LATIN SMALL LETTER U WITH OGONEK
2580;upblock;UPPER HALF BLOCK
03C5;upsilon;GREEK SMALL LETTER UPSILON
03CB;upsilondieresis;GREEK SMALL LETTER UPSILON WITH DIALYTIKA
03B0;upsilondieresistonos;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
03CD;upsilontonos;GREEK SMALL LETTER UPSILON WITH TONOS
016F;uring;LATIN SMALL LETTER U WITH RING ABOVE
0169;utilde;LATIN SMALL LETTER U WITH TILDE
0076;v;LATIN SMALL LETTER V
0077;w;LATIN SMALL LETTER W
1E83;wacute;LATIN SMALL LETTER W WITH ACUTE
0175;wcircumflex;LATIN SMALL LETTER W WITH CIRCUMFLEX
1E85;wdieresis;LATIN SMALL LETTER W WITH DIAERESIS
2118;weierstrass;SCRIPT CAPITAL P
1E81;wgrave;LATIN SMALL LETTER W WITH GRAVE
0078;x;LATIN SMALL LETTER X
03BE;xi;GREEK SMALL LETTER XI
0079;y;LATIN SMALL LETTER Y
00FD;yacute;LATIN SMALL LETTER Y WITH ACUTE
0177;ycircumflex;LATIN SMALL LETTER Y WITH CIRCUMFLEX
00FF;ydieresis;LATIN SMALL LETTER Y WITH DIAERESIS
00A5;yen;YEN SIGN
1EF3;ygrave;LATIN SMALL LETTER Y WITH GRAVE
007A;z;LATIN SMALL LETTER Z
017A;zacute;LATIN SMALL LETTER Z WITH ACUTE
017E;zcaron;LATIN SMALL LETTER Z WITH CARON
017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE
0030;zero;DIGIT ZERO
03B6;zeta;GREEK SMALL LETTER ZETA
#END

141
util/aglfn/main.cpp Normal file
View File

@ -0,0 +1,141 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qbytearray.h>
#include <qlist.h>
#include <qmap.h>
#include <qfile.h>
static QByteArray fileVersion;
static QList<QByteArray> glyphNames;
static QList<ushort> glyphNameOffsets;
static QMap<ushort, ushort> uni_to_agl_map; // sort by code point for bsearch
static void readGlyphList()
{
qDebug("Reading aglfn.txt:");
QFile f("data/aglfn.txt");
if (!f.exists())
qFatal("Couldn't find aglfn.txt");
f.open(QFile::ReadOnly);
glyphNames.append(".notdef");
glyphNameOffsets.append(0);
uni_to_agl_map.insert(0, 0);
while (!f.atEnd()) {
QByteArray line;
line.resize(1024);
int len = f.readLine(line.data(), 1024);
line.resize(len-1);
int comment = line.indexOf('#');
if (comment != -1) {
if (fileVersion.isEmpty()) {
int commentTableVersion = line.indexOf("Table version:", comment);
if (commentTableVersion != -1)
fileVersion = line.mid(commentTableVersion + 15).trimmed();
}
line = line.left(comment);
}
line = line.trimmed();
if (line.isEmpty())
continue;
QList<QByteArray> l = line.split(';');
Q_ASSERT(l.size() == 3);
bool ok;
ushort codepoint = l[0].toUShort(&ok, 16);
Q_ASSERT(ok);
QByteArray glyphName = l[1].trimmed();
int glyphIndex = glyphNames.indexOf(glyphName);
if (glyphIndex == -1) {
glyphIndex = glyphNames.size();
glyphNameOffsets.append(glyphNameOffsets.last() + glyphNames.last().size() + 1);
glyphNames.append(glyphName);
}
uni_to_agl_map.insert(codepoint, glyphIndex);
}
qDebug(" %d unique glyph names found", glyphNames.size());
}
static QByteArray createGlyphList()
{
qDebug("createGlyphList:");
QByteArray out;
out += "static const char glyph_names[] =\n\"";
int linelen = 2;
for (int i = 0; i < glyphNames.size(); ++i) {
if (linelen + glyphNames.at(i).size() + 2 >= 80) {
linelen = 2;
out += "\"\n\"";
}
linelen += glyphNames.at(i).size() + 2;
out += glyphNames.at(i) + "\\0";
}
if (out.endsWith("\""))
out.chop(2);
out += "\"\n;\n\n";
out += "struct AGLEntry {\n"
" unsigned short uc;\n"
" unsigned short index;\n"
"};\n"
"\n"
"inline bool operator<(unsigned short uc, AGLEntry entry)\n"
"{ return uc < entry.uc; }\n"
"inline bool operator<(AGLEntry entry, unsigned short uc)\n"
"{ return entry.uc < uc; }\n"
"\n"
"static const AGLEntry unicode_to_agl_map[] = {";
int i = 0;
QMap<ushort, ushort>::const_iterator it = uni_to_agl_map.constBegin();
while (it != uni_to_agl_map.constEnd()) {
if (i++ % 4 == 0)
out += "\n ";
out += " { 0x" + QByteArray::number(it.key(), 16).rightJustified(4, '0') + ", ";
out += QByteArray::number(glyphNameOffsets.at(it.value())).leftJustified(4, ' ') + " },";
++it;
}
out.chop(1);
out += "\n};\n\n";
out += "enum { unicode_to_agl_map_size = sizeof(unicode_to_agl_map) / sizeof(unicode_to_agl_map[0]) };\n\n";
qDebug(" Glyph names list uses : %d bytes", glyphNameOffsets.last() + glyphNames.last().size() + 1);
qDebug(" Unicode to Glyph names map uses : %d bytes", uni_to_agl_map.size()*4);
return out;
}
int main(int, char **)
{
readGlyphList();
QByteArray header =
"// Copyright (C) 2016 The Qt Company Ltd.\n"
"// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only\n"
"\n";
QByteArray note =
"/* This file is autogenerated from the Adobe Glyph List database" +
(!fileVersion.isEmpty() ? " v" + fileVersion : "") + ". Do not edit */\n\n";
QFile f("../../src/gui/text/qfontsubset_agl.cpp");
f.open(QFile::WriteOnly|QFile::Truncate);
f.write(header);
f.write(note);
f.write("namespace {\n\n");
f.write(createGlyphList());
f.write("}\n");
f.close();
}

View File

@ -0,0 +1,106 @@
#!/bin/bash
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
# This util launches the Android emulator and ensures it doesn't stuck/freeze
# by detecting that and restarting it
set -e
EMULATOR_MAX_RETRIES=5
EMULATOR_EXEC="$ANDROID_SDK_ROOT/emulator/emulator"
ADB_EXEC="$ANDROID_SDK_ROOT/platform-tools/adb"
if [ -z "${ANDROID_EMULATOR}" ]
then
EMULATOR_NAME="@emulator_x86_api_23"
else
EMULATOR_NAME="$ANDROID_EMULATOR"
fi
function check_for_android_device
{
$ADB_EXEC devices \
| awk 'NR==2{print $2}' | grep -qE '^(online|device)$'
}
# WARNING: On the very first boot of the emulator it happens that the device
# "finishes" booting and getprop shows bootanim=stopped and
# boot_completed=1. But sometimes not all packages have been installed (`pm
# list packages` shows only 16 packages installed), and after around half a
# minute the boot animation starts spinning (bootanim=running) again despite
# boot_completed=1 all the time. After some minutes the boot animation stops
# again and the list of packages contains 80 packages. Only then the device is
# fully booted, and only then is dev.bootcomplete=1.
#
# To reproduce the emulator booting as the first time, you have to delete the
# cached images found inside $HOME/.android especially the
# "userdata-qemu.img.qcow2" file.
function check_if_fully_booted
{
# The "getprop" command separates lines with \r\n so we trim them
bootanim=` $ADB_EXEC shell getprop init.svc.bootanim | tr -d '\r\n'`
boot_completed=`$ADB_EXEC shell getprop sys.boot_completed | tr -d '\r\n'`
bootcomplete=` $ADB_EXEC shell getprop dev.bootcomplete | tr -d '\r\n'`
echo "bootanim=$bootanim boot_completed=$boot_completed bootcomplete=$bootcomplete"
[ "$bootanim" = stopped ] && [ "$boot_completed" = 1 ] && [ "$bootcomplete" = 1 ]
}
for counter in `seq ${EMULATOR_MAX_RETRIES}`
do
$ADB_EXEC start-server
if check_for_android_device
then
echo "Emulator is already running but it shouldn't be. Aborting\!"
exit 3
fi
echo "Starting emulator, try ${counter}/${EMULATOR_MAX_RETRIES}"
$EMULATOR_EXEC $EMULATOR_NAME \
-gpu swiftshader_indirect -no-audio -partition-size 4096 \
-cores 4 -memory 16000 -no-snapshot-load -no-snapshot-save \
</dev/null >$HOME/emulator.log 2>&1 &
emulator_pid=$!
disown $emulator_pid
echo "Waiting for emulated device to appear..."
$ADB_EXEC wait-for-device
echo "Waiting a few minutes for the emulator to fully boot..."
emulator_status=down
for i in `seq 300`
do
sleep 1
if check_for_android_device && check_if_fully_booted
then
emulator_status=up
break
fi
done
# If emulator status is still offline after timeout period,
# we can assume it's stuck, and we must restart it
if [ $emulator_status = up ]
then
echo "Emulator started successfully"
break
else
if [ $counter -lt $EMULATOR_MAX_RETRIES ]
then
echo "Emulator failed to start, forcefully killing current instance and re-starting emulator"
kill $emulator_pid || true
sleep 5
elif [ $counter -eq $EMULATOR_MAX_RETRIES ]
then
echo "Emulator failed to start, reached maximum number of retries. Aborting\!"
exit 2
fi
fi
done
exit 0

20
util/cmake/Makefile Normal file
View File

@ -0,0 +1,20 @@
test: flake8 mypy pytest black_format_check
coverage:
pytest --cov .
format:
black *.py --line-length 100
black_format_check:
black *.py --line-length 100 --check
flake8:
flake8 *.py --ignore=E501,E266,E203,W503,F541
pytest:
pytest
mypy:
mypy --pretty *.py

18
util/cmake/Pipfile Normal file
View File

@ -0,0 +1,18 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
pyparsing = "*"
sympy = "*"
mypy = "*"
pytest = "*"
pytest-cov = "*"
flake8 = "*"
portalocker = "*"
[dev-packages]
[requires]
python_version = "3.7"

57
util/cmake/README.md Normal file
View File

@ -0,0 +1,57 @@
# CMake Utils
This directory holds scripts to help the porting process from `qmake` to `cmake` for Qt6.
If you're looking to port your own Qt-based project from `qmake` to `cmake`, please use
[qmake2cmake](https://wiki.qt.io/Qmake2cmake).
# Requirements
* [Python 3.7](https://www.python.org/downloads/),
* `pipenv` or `pip` to manage the modules.
## Python modules
Since Python has many ways of handling projects, you have a couple of options to
install the dependencies of the scripts:
### Using `pipenv`
The dependencies are specified on the `Pipfile`, so you just need to run
`pipenv install` and that will automatically create a virtual environment
that you can activate with a `pipenv shell`.
### Using `pip`
It's highly recommended to use a [virtualenvironment](https://virtualenv.pypa.io/en/latest/)
to avoid conflict with other packages that are already installed: `pip install virtualenv`.
* Create an environment: `virtualenv env`,
* Activate the environment: `source env/bin/activate`
(on Windows: `source env\Scripts\activate.bat`)
* Install the requirements: `pip install -r requirements.txt`
If the `pip install` command above doesn't work, try:
```
python3.7 -m pip install -r requirements.txt
```
# Contributing to the scripts
You can verify if the styling of a script is compliant with PEP8, with a couple of exceptions:
Install [flake8](http://flake8.pycqa.org/en/latest/) (`pip install flake8`) and run it
on all python source files:
```
make flake8
```
You can also modify the file with an automatic formatter,
like [black](https://black.readthedocs.io/en/stable/) (`pip install black`),
and execute it:
```
make format
```

View File

@ -0,0 +1,116 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from argparse import ArgumentParser
import os
import re
import subprocess
import typing
def _parse_commandline():
parser = ArgumentParser(description="Calculate the conversion rate to cmake.")
parser.add_argument("--debug", dest="debug", action="store_true", help="Turn on debug output")
parser.add_argument(
"source_directory",
metavar="<Source Directory>",
type=str,
help="The Qt module source directory",
)
parser.add_argument(
"binary_directory",
metavar="<CMake build directory>",
type=str,
help="The CMake build directory (might be empty)",
)
return parser.parse_args()
def calculate_baseline(source_directory: str, *, debug: bool = False) -> int:
if debug:
print(f'Scanning "{source_directory}" for qmake-based tests.')
result = subprocess.run(
'/usr/bin/git grep -E "^\\s*CONFIG\\s*\\+?=.*\\btestcase\\b" | sort -u | wc -l',
shell=True,
capture_output=True,
cwd=source_directory,
)
return int(result.stdout)
def build(source_directory: str, binary_directory: str, *, debug=False) -> None:
abs_source = os.path.abspath(source_directory)
if not os.path.isdir(binary_directory):
os.makedirs(binary_directory)
if not os.path.exists(os.path.join(binary_directory, "CMakeCache.txt")):
if debug:
print(f'Running cmake in "{binary_directory}"')
result = subprocess.run(["/usr/bin/cmake", "-GNinja", abs_source], cwd=binary_directory)
if debug:
print(f"CMake return code: {result.returncode}.")
assert result.returncode == 0
if debug:
print(f'Running ninja in "{binary_directory}".')
result = subprocess.run("/usr/bin/ninja", cwd=binary_directory)
if debug:
print(f"Ninja return code: {result.returncode}.")
assert result.returncode == 0
def test(binary_directory: str, *, debug=False) -> typing.Tuple[int, int]:
if debug:
print(f'Running ctest in "{binary_directory}".')
result = subprocess.run(
'/usr/bin/ctest -j 250 | grep "tests passed, "',
shell=True,
capture_output=True,
cwd=binary_directory,
)
summary = result.stdout.decode("utf-8").replace("\n", "")
if debug:
print(f"Test summary: {summary} ({result.returncode}).")
matches = re.fullmatch(r"\d+% tests passed, (\d+) tests failed out of (\d+)", summary)
if matches:
if debug:
print(f"Matches: failed {matches.group(1)}, total {matches.group(2)}.")
return (int(matches.group(2)), int(matches.group(2)) - int(matches.group(1)))
return (0, 0)
def main() -> int:
args = _parse_commandline()
base_line = calculate_baseline(args.source_directory, debug=args.debug)
if base_line <= 0:
print(f"Could not find the qmake baseline in {args.source_directory}.")
return 1
if args.debug:
print(f"qmake baseline: {base_line} test binaries.")
cmake_total = 0
cmake_success = 0
try:
build(args.source_directory, args.binary_directory, debug=args.debug)
(cmake_total, cmake_success) = test(args.binary_directory, debug=args.debug)
finally:
if cmake_total == 0:
print("\n\n\nCould not calculate the cmake state.")
return 2
else:
print(f"\n\n\nCMake test conversion rate: {cmake_total/base_line:.2f}.")
print(f"CMake test success rate : {cmake_success/base_line:.2f}.")
return 0
if __name__ == "__main__":
main()

View File

@ -0,0 +1,211 @@
#!/usr/bin/env python3
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import re
from sympy import simplify_logic, And, Or, Not, SympifyError # type: ignore
from condition_simplifier_cache import simplify_condition_memoize
def _iterate_expr_tree(expr, op, matches):
assert expr.func == op
keepers = ()
for arg in expr.args:
if arg in matches:
matches = tuple(x for x in matches if x != arg)
elif arg == op:
(matches, extra_keepers) = _iterate_expr_tree(arg, op, matches)
keepers = (*keepers, *extra_keepers)
else:
keepers = (*keepers, arg)
return matches, keepers
def _simplify_expressions(expr, op, matches, replacement):
for arg in expr.args:
expr = expr.subs(arg, _simplify_expressions(arg, op, matches, replacement))
if expr.func == op:
(to_match, keepers) = tuple(_iterate_expr_tree(expr, op, matches))
if len(to_match) == 0:
# build expression with keepers and replacement:
if keepers:
start = replacement
current_expr = None
last_expr = keepers[-1]
for repl_arg in keepers[:-1]:
current_expr = op(start, repl_arg)
start = current_expr
top_expr = op(start, last_expr)
else:
top_expr = replacement
expr = expr.subs(expr, top_expr)
return expr
def _simplify_flavors_in_condition(base: str, flavors, expr):
"""Simplify conditions based on the knowledge of which flavors
belong to which OS."""
base_expr = simplify_logic(base)
false_expr = simplify_logic("false")
for flavor in flavors:
flavor_expr = simplify_logic(flavor)
expr = _simplify_expressions(expr, And, (base_expr, flavor_expr), flavor_expr)
expr = _simplify_expressions(expr, Or, (base_expr, flavor_expr), base_expr)
expr = _simplify_expressions(expr, And, (Not(base_expr), flavor_expr), false_expr)
return expr
def _simplify_os_families(expr, family_members, other_family_members):
for family in family_members:
for other in other_family_members:
if other in family_members:
continue # skip those in the sub-family
f_expr = simplify_logic(family)
o_expr = simplify_logic(other)
expr = _simplify_expressions(expr, And, (f_expr, Not(o_expr)), f_expr)
expr = _simplify_expressions(expr, And, (Not(f_expr), o_expr), o_expr)
expr = _simplify_expressions(expr, And, (f_expr, o_expr), simplify_logic("false"))
return expr
def _recursive_simplify(expr):
"""Simplify the expression as much as possible based on
domain knowledge."""
input_expr = expr
# Simplify even further, based on domain knowledge:
# windowses = ('WIN32', 'WINRT')
apples = ("MACOS", "UIKIT", "IOS", "TVOS", "WATCHOS")
bsds = ("FREEBSD", "OPENBSD", "NETBSD")
androids = ("ANDROID",)
unixes = (
"APPLE",
*apples,
"BSD",
*bsds,
"LINUX",
*androids,
"HAIKU",
"INTEGRITY",
"VXWORKS",
"QNX",
"WASM",
)
unix_expr = simplify_logic("UNIX")
win_expr = simplify_logic("WIN32")
false_expr = simplify_logic("false")
true_expr = simplify_logic("true")
expr = expr.subs(Not(unix_expr), win_expr) # NOT UNIX -> WIN32
expr = expr.subs(Not(win_expr), unix_expr) # NOT WIN32 -> UNIX
# UNIX [OR foo ]OR WIN32 -> ON [OR foo]
expr = _simplify_expressions(expr, Or, (unix_expr, win_expr), true_expr)
# UNIX [AND foo ]AND WIN32 -> OFF [AND foo]
expr = _simplify_expressions(expr, And, (unix_expr, win_expr), false_expr)
expr = _simplify_flavors_in_condition("WIN32", ("WINRT",), expr)
expr = _simplify_flavors_in_condition("APPLE", apples, expr)
expr = _simplify_flavors_in_condition("BSD", bsds, expr)
expr = _simplify_flavors_in_condition("UNIX", unixes, expr)
# Simplify families of OSes against other families:
expr = _simplify_os_families(expr, ("WIN32", "WINRT"), unixes)
expr = _simplify_os_families(expr, androids, unixes)
expr = _simplify_os_families(expr, ("BSD", *bsds), unixes)
for family in ("HAIKU", "QNX", "INTEGRITY", "LINUX", "VXWORKS"):
expr = _simplify_os_families(expr, (family,), unixes)
# Now simplify further:
expr = simplify_logic(expr)
while expr != input_expr:
input_expr = expr
expr = _recursive_simplify(expr)
return expr
@simplify_condition_memoize
def simplify_condition(condition: str) -> str:
input_condition = condition.strip()
# Map to sympy syntax:
condition = " " + input_condition + " "
condition = condition.replace("(", " ( ")
condition = condition.replace(")", " ) ")
tmp = ""
while tmp != condition:
tmp = condition
condition = condition.replace(" NOT ", " ~ ")
condition = condition.replace(" AND ", " & ")
condition = condition.replace(" OR ", " | ")
condition = condition.replace(" ON ", " true ")
condition = condition.replace(" OFF ", " false ")
# Replace dashes with a token
condition = condition.replace("-", "_dash_")
# SymPy chokes on expressions that contain two tokens one next to
# the other delimited by a space, which are not an operation.
# So a CMake condition like "TARGET Foo::Bar" fails the whole
# expression simplifying process.
# Turn these conditions into a single token so that SymPy can parse
# the expression, and thus simplify it.
# Do this by replacing and keeping a map of conditions to single
# token symbols.
# Support both target names without double colons, and with double
# colons.
pattern = re.compile(r"(TARGET [a-zA-Z]+(?:::[a-zA-Z]+)?)")
target_symbol_mapping = {}
all_target_conditions = re.findall(pattern, condition)
for target_condition in all_target_conditions:
# Replace spaces and colons with underscores.
target_condition_symbol_name = re.sub("[ :]", "_", target_condition)
target_symbol_mapping[target_condition_symbol_name] = target_condition
condition = re.sub(target_condition, target_condition_symbol_name, condition)
# Do similar token mapping for comparison operators.
pattern = re.compile(r"([a-zA-Z_0-9]+ (?:STRLESS|STREQUAL|STRGREATER) [a-zA-Z_0-9]+)")
comparison_symbol_mapping = {}
all_comparisons = re.findall(pattern, condition)
for comparison in all_comparisons:
# Replace spaces and colons with underscores.
comparison_symbol_name = re.sub("[ ]", "_", comparison)
comparison_symbol_mapping[comparison_symbol_name] = comparison
condition = re.sub(comparison, comparison_symbol_name, condition)
try:
# Generate and simplify condition using sympy:
condition_expr = simplify_logic(condition)
condition = str(_recursive_simplify(condition_expr))
# Restore the target conditions.
for symbol_name in target_symbol_mapping:
condition = re.sub(symbol_name, target_symbol_mapping[symbol_name], condition)
# Restore comparisons.
for comparison in comparison_symbol_mapping:
condition = re.sub(comparison, comparison_symbol_mapping[comparison], condition)
# Map back to CMake syntax:
condition = condition.replace("~", "NOT ")
condition = condition.replace("&", "AND")
condition = condition.replace("|", "OR")
condition = condition.replace("True", "ON")
condition = condition.replace("False", "OFF")
condition = condition.replace("_dash_", "-")
except (SympifyError, TypeError, AttributeError):
# sympy did not like our input, so leave this condition alone:
condition = input_condition
return condition or "ON"

View File

@ -0,0 +1,154 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import atexit
import hashlib
import json
import os
import sys
import time
from typing import Any, Callable, Dict
condition_simplifier_cache_enabled = True
def set_condition_simplified_cache_enabled(value: bool):
global condition_simplifier_cache_enabled
condition_simplifier_cache_enabled = value
def get_current_file_path() -> str:
try:
this_file = __file__
except NameError:
this_file = sys.argv[0]
this_file = os.path.abspath(this_file)
return this_file
def get_cache_location() -> str:
this_file = get_current_file_path()
dir_path = os.path.dirname(this_file)
cache_path = os.path.join(dir_path, ".pro2cmake_cache", "cache.json")
return cache_path
def get_file_checksum(file_path: str) -> str:
try:
with open(file_path, "r") as content_file:
content = content_file.read()
except IOError:
content = str(time.time())
checksum = hashlib.md5(content.encode("utf-8")).hexdigest()
return checksum
def get_condition_simplifier_checksum() -> str:
current_file_path = get_current_file_path()
dir_name = os.path.dirname(current_file_path)
condition_simplifier_path = os.path.join(dir_name, "condition_simplifier.py")
return get_file_checksum(condition_simplifier_path)
def init_cache_dict():
return {
"checksum": get_condition_simplifier_checksum(),
"schema_version": "1",
"cache": {"conditions": {}},
}
def merge_dicts_recursive(a: Dict[str, Any], other: Dict[str, Any]) -> Dict[str, Any]:
"""Merges values of "other" into "a", mutates a."""
for key in other:
if key in a:
if isinstance(a[key], dict) and isinstance(other[key], dict):
merge_dicts_recursive(a[key], other[key])
elif a[key] == other[key]:
pass
else:
a[key] = other[key]
return a
def open_file_safe(file_path: str, mode: str = "r+"):
# Use portalocker package for file locking if available,
# otherwise print a message to install the package.
try:
import portalocker # type: ignore
return portalocker.Lock(file_path, mode=mode, flags=portalocker.LOCK_EX)
except ImportError:
print(
"The conversion script is missing a required package: portalocker. Please run "
"python -m pip install -r requirements.txt to install the missing dependency."
)
exit(1)
def simplify_condition_memoize(f: Callable[[str], str]):
cache_path = get_cache_location()
cache_file_content: Dict[str, Any] = {}
if os.path.exists(cache_path):
try:
with open_file_safe(cache_path, mode="r") as cache_file:
cache_file_content = json.load(cache_file)
except (IOError, ValueError):
print(f"Invalid pro2cmake cache file found at: {cache_path}. Removing it.")
os.remove(cache_path)
if not cache_file_content:
cache_file_content = init_cache_dict()
current_checksum = get_condition_simplifier_checksum()
if cache_file_content["checksum"] != current_checksum:
cache_file_content = init_cache_dict()
def update_cache_file():
if not os.path.exists(cache_path):
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
# Create the file if it doesn't exist, but don't override
# it.
with open(cache_path, "a"):
pass
updated_cache = cache_file_content
with open_file_safe(cache_path, "r+") as cache_file_write_handle:
# Read any existing cache content, and truncate the file.
cache_file_existing_content = cache_file_write_handle.read()
cache_file_write_handle.seek(0)
cache_file_write_handle.truncate()
# Merge the new cache into the old cache if it exists.
if cache_file_existing_content:
possible_cache = json.loads(cache_file_existing_content)
if (
"checksum" in possible_cache
and "schema_version" in possible_cache
and possible_cache["checksum"] == cache_file_content["checksum"]
and possible_cache["schema_version"] == cache_file_content["schema_version"]
):
updated_cache = merge_dicts_recursive(dict(possible_cache), updated_cache)
json.dump(updated_cache, cache_file_write_handle, indent=4)
# Flush any buffered writes.
cache_file_write_handle.flush()
os.fsync(cache_file_write_handle.fileno())
atexit.register(update_cache_file)
def helper(condition: str) -> str:
if (
condition not in cache_file_content["cache"]["conditions"]
or not condition_simplifier_cache_enabled
):
cache_file_content["cache"]["conditions"][condition] = f(condition)
return cache_file_content["cache"]["conditions"][condition]
return helper

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
#!/usr/bin/bash
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
pro_files=$(find . -name \*.pro)
for f in ${pro_files}; do
if grep "^load(qt_module)" "${f}" > /dev/null ; then
target=$(grep "TARGET" "${f}" | cut -d'=' -f2 | sed -e "s/\s*//g")
module=$(basename ${f})
echo "'${module%.pro}': '${target}',"
fi
done

869
util/cmake/helper.py Normal file
View File

@ -0,0 +1,869 @@
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import re
import typing
class LibraryMapping:
def __init__(
self,
soName: str,
packageName: typing.Optional[str],
targetName: typing.Optional[str],
*,
resultVariable: typing.Optional[str] = None,
extra: typing.List[str] = [],
components: typing.Optional[typing.List[str]] = None,
appendFoundSuffix: bool = True,
emit_if: str = "",
is_bundled_with_qt: bool = False,
test_library_overwrite: str = "",
run_library_test: bool = False,
no_link_so_name: str = "",
) -> None:
self.soName = soName
self.packageName = packageName
self.resultVariable = resultVariable
self.appendFoundSuffix = appendFoundSuffix
# Allows passing addiitonal arguments to the generated find_package call.
self.extra = extra
self.components = components
self.targetName = targetName
# True if qt bundles the library sources as part of Qt.
self.is_bundled_with_qt = is_bundled_with_qt
# if emit_if is non-empty, the generated find_package call
# for a library will be surrounded by this condition.
self.emit_if = emit_if
# Allow overwriting library name when used with tests. E.g.: _nolink
# targets do not exist when used during compile tests
self.test_library_overwrite = test_library_overwrite
# Run the library compile test of configure.json
self.run_library_test = run_library_test
# The custom nolink library mapping associated with this one.
self.no_link_so_name = no_link_so_name
def is_qt(self) -> bool:
return self.packageName == "Qt" or self.packageName == "Qt5" or self.packageName == "Qt6"
_qt_library_map = [
# Qt:
LibraryMapping("androidextras", "Qt6", "Qt::AndroidExtras", components=["AndroidExtras"]),
LibraryMapping("3danimation", "Qt6", "Qt::3DAnimation", components=["3DAnimation"]),
LibraryMapping("3dcore", "Qt6", "Qt::3DCore", components=["3DCore"]),
LibraryMapping("3dcoretest", "Qt6", "Qt::3DCoreTest", components=["3DCoreTest"]),
LibraryMapping("3dextras", "Qt6", "Qt::3DExtras", components=["3DExtras"]),
LibraryMapping("3dinput", "Qt6", "Qt::3DInput", components=["3DInput"]),
LibraryMapping("3dlogic", "Qt6", "Qt::3DLogic", components=["3DLogic"]),
LibraryMapping("3dquick", "Qt6", "Qt::3DQuick", components=["3DQuick"]),
LibraryMapping("3dquickextras", "Qt6", "Qt::3DQuickExtras", components=["3DQuickExtras"]),
LibraryMapping("3dquickinput", "Qt6", "Qt::3DQuickInput", components=["3DQuickInput"]),
LibraryMapping("3dquickrender", "Qt6", "Qt::3DQuickRender", components=["3DQuickRender"]),
LibraryMapping("3drender", "Qt6", "Qt::3DRender", components=["3DRender"]),
LibraryMapping(
"application-lib", "Qt6", "Qt::AppManApplication", components=["AppManApplication"]
),
LibraryMapping("axbase", "Qt6", "Qt::AxBasePrivate", components=["AxBasePrivate"]),
LibraryMapping("axcontainer", "Qt6", "Qt::AxContainer", components=["AxContainer"]),
LibraryMapping("axserver", "Qt6", "Qt::AxServer", components=["AxServer"]),
LibraryMapping("bluetooth", "Qt6", "Qt::Bluetooth", components=["Bluetooth"]),
LibraryMapping("bootstrap", "Qt6", "Qt::Bootstrap", components=["Bootstrap"]),
# bootstrap-dbus: Not needed in Qt6!
LibraryMapping("client", "Qt6", "Qt::WaylandClient", components=["WaylandClient"]),
LibraryMapping("coap", "Qt6", "Qt::Coap", components=["Coap"]),
LibraryMapping("common-lib", "Qt6", "Qt::AppManCommon", components=["AppManCommon"]),
LibraryMapping("compositor", "Qt6", "Qt::WaylandCompositor", components=["WaylandCompositor"]),
LibraryMapping("concurrent", "Qt6", "Qt::Concurrent", components=["Concurrent"]),
LibraryMapping("container", "Qt6", "Qt::AxContainer", components=["AxContainer"]),
LibraryMapping("control", "Qt6", "Qt::AxServer", components=["AxServer"]),
LibraryMapping("core_headers", "Qt6", "Qt::WebEngineCore", components=["WebEngineCore"]),
LibraryMapping("core", "Qt6", "Qt::Core", components=["Core"]),
LibraryMapping("crypto-lib", "Qt6", "Qt::AppManCrypto", components=["AppManCrypto"]),
LibraryMapping("dbus", "Qt6", "Qt::DBus", components=["DBus"]),
LibraryMapping("designer", "Qt6", "Qt::Designer", components=["Designer"]),
LibraryMapping(
"designercomponents",
"Qt6",
"Qt::DesignerComponentsPrivate",
components=["DesignerComponentsPrivate"],
),
LibraryMapping(
"devicediscovery",
"Qt6",
"Qt::DeviceDiscoverySupportPrivate",
components=["DeviceDiscoverySupportPrivate"],
),
LibraryMapping(
"devicediscovery_support",
"Qt6",
"Qt::DeviceDiscoverySupportPrivate",
components=["DeviceDiscoverySupportPrivate"],
),
LibraryMapping("edid", "Qt6", "Qt::EdidSupport", components=["EdidSupport"]),
LibraryMapping("edid_support", "Qt6", "Qt::EdidSupport", components=["EdidSupport"]),
LibraryMapping("eglconvenience", "Qt6", "Qt::EglSupport", components=["EglSupport"]),
LibraryMapping(
"eglfsdeviceintegration",
"Qt6",
"Qt::EglFSDeviceIntegrationPrivate",
components=["EglFSDeviceIntegrationPrivate"],
),
LibraryMapping(
"eglfs_kms_support",
"Qt6",
"Qt::EglFsKmsSupportPrivate",
components=["EglFsKmsSupportPrivate"],
),
LibraryMapping(
"eglfs_kms_gbm_support",
"Qt6",
"Qt::EglFsKmsGbmSupportPrivate",
components=["EglFsKmsGbmSupportPrivate"],
),
LibraryMapping("egl_support", "Qt6", "Qt::EglSupport", components=["EglSupport"]),
# enginio: Not needed in Qt6!
LibraryMapping(
"eventdispatchers",
"Qt6",
"Qt::EventDispatcherSupport",
components=["EventDispatcherSupport"],
),
LibraryMapping(
"eventdispatcher_support",
"Qt6",
"Qt::EventDispatcherSupport",
components=["EventDispatcherSupport"],
),
LibraryMapping("fbconvenience", "Qt6", "Qt::FbSupportPrivate", components=["FbSupportPrivate"]),
LibraryMapping("fb_support", "Qt6", "Qt::FbSupportPrivate", components=["FbSupportPrivate"]),
LibraryMapping(
"fontdatabase_support",
"Qt6",
"Qt::FontDatabaseSupport",
components=["FontDatabaseSupport"],
),
LibraryMapping("gamepad", "Qt6", "Qt::Gamepad", components=["Gamepad"]),
LibraryMapping("geniviextras", "Qt6", "Qt::GeniviExtras", components=["GeniviExtras"]),
LibraryMapping("global", "Qt6", "Qt::Core", components=["Core"]), # manually added special case
LibraryMapping("glx_support", "Qt6", "Qt::GlxSupport", components=["GlxSupport"]),
LibraryMapping("gsttools", "Qt6", "Qt::MultimediaGstTools", components=["MultimediaGstTools"]),
LibraryMapping("gui", "Qt6", "Qt::Gui", components=["Gui"]),
LibraryMapping("help", "Qt6", "Qt::Help", components=["Help"]),
LibraryMapping(
"hunspellinputmethod",
"Qt6",
"Qt::HunspellInputMethodPrivate",
components=["HunspellInputMethodPrivate"],
),
LibraryMapping("input", "Qt6", "Qt::InputSupportPrivate", components=["InputSupportPrivate"]),
LibraryMapping(
"input_support",
"Qt6",
"Qt::InputSupportPrivate",
components=["InputSupportPrivate"],
),
LibraryMapping("installer-lib", "Qt6", "Qt::AppManInstaller", components=["AppManInstaller"]),
LibraryMapping("ivi", "Qt6", "Qt::Ivi", components=["Ivi"]),
LibraryMapping("ivicore", "Qt6", "Qt::IviCore", components=["IviCore"]),
LibraryMapping("ivimedia", "Qt6", "Qt::IviMedia", components=["IviMedia"]),
LibraryMapping("knx", "Qt6", "Qt::Knx", components=["Knx"]),
LibraryMapping(
"kmsconvenience", "Qt6", "Qt::KmsSupportPrivate", components=["KmsSupportPrivate"]
),
LibraryMapping("kms_support", "Qt6", "Qt::KmsSupportPrivate", components=["KmsSupportPrivate"]),
LibraryMapping("launcher-lib", "Qt6", "Qt::AppManLauncher", components=["AppManLauncher"]),
LibraryMapping("lib", "Qt6", "Qt::Designer", components=["Designer"]),
LibraryMapping(
"linuxaccessibility_support",
"Qt6",
"Qt::LinuxAccessibilitySupport",
components=["LinuxAccessibilitySupport"],
),
LibraryMapping("location", "Qt6", "Qt::Location", components=["Location"]),
LibraryMapping("macextras", "Qt6", "Qt::MacExtras", components=["MacExtras"]),
LibraryMapping("main-lib", "Qt6", "Qt::AppManMain", components=["AppManMain"]),
LibraryMapping("manager-lib", "Qt6", "Qt::AppManManager", components=["AppManManager"]),
LibraryMapping("monitor-lib", "Qt6", "Qt::AppManMonitor", components=["AppManMonitor"]),
LibraryMapping("mqtt", "Qt6", "Qt::Mqtt", components=["Mqtt"]),
LibraryMapping("multimedia", "Qt6", "Qt::Multimedia", components=["Multimedia"]),
LibraryMapping(
"multimediawidgets",
"Qt6",
"Qt::MultimediaWidgets",
components=["MultimediaWidgets"],
),
LibraryMapping("network", "Qt6", "Qt::Network", components=["Network"]),
LibraryMapping("networkauth", "Qt6", "Qt::NetworkAuth", components=["NetworkAuth"]),
LibraryMapping("nfc", "Qt6", "Qt::Nfc", components=["Nfc"]),
LibraryMapping("oauth", "Qt6", "Qt::NetworkAuth", components=["NetworkAuth"]),
LibraryMapping("opcua", "Qt6", "Qt::OpcUa", components=["OpcUa"]),
LibraryMapping("opcua_private", "Qt6", "Qt::OpcUaPrivate", components=["OpcUaPrivate"]),
LibraryMapping("opengl", "Qt6", "Qt::OpenGL", components=["OpenGL"]),
LibraryMapping("openglwidgets", "Qt6", "Qt::OpenGLWidgets", components=["OpenGLWidgets"]),
LibraryMapping("package-lib", "Qt6", "Qt::AppManPackage", components=["AppManPackage"]),
LibraryMapping(
"packetprotocol",
"Qt6",
"Qt::PacketProtocolPrivate",
components=["PacketProtocolPrivate"],
),
LibraryMapping(
"particles",
"Qt6",
"Qt::QuickParticlesPrivate",
components=["QuickParticlesPrivate"],
),
LibraryMapping(
"plugin-interfaces",
"Qt6",
"Qt::AppManPluginInterfaces",
components=["AppManPluginInterfaces"],
),
LibraryMapping("positioning", "Qt6", "Qt::Positioning", components=["Positioning"]),
LibraryMapping(
"positioningquick", "Qt6", "Qt::PositioningQuick", components=["PositioningQuick"]
),
LibraryMapping("printsupport", "Qt6", "Qt::PrintSupport", components=["PrintSupport"]),
LibraryMapping("purchasing", "Qt6", "Qt::Purchasing", components=["Purchasing"]),
LibraryMapping("qmldebug", "Qt6", "Qt::QmlDebugPrivate", components=["QmlDebugPrivate"]),
LibraryMapping(
"qmldevtools", "Qt6", "Qt::QmlDevToolsPrivate", components=["QmlDevToolsPrivate"]
),
LibraryMapping(
"qmlcompiler", "Qt6", "Qt::QmlCompilerPrivate", components=["QmlCompilerPrivate"]
),
LibraryMapping("qml", "Qt6", "Qt::Qml", components=["Qml"]),
LibraryMapping("qmldom", "Qt6", "Qt::QmlDomPrivate", components=["QmlDomPrivate"]),
LibraryMapping("qmlmodels", "Qt6", "Qt::QmlModels", components=["QmlModels"]),
LibraryMapping("qmltest", "Qt6", "Qt::QuickTest", components=["QuickTest"]),
LibraryMapping(
"qtmultimediaquicktools",
"Qt6",
"Qt::MultimediaQuickPrivate",
components=["MultimediaQuickPrivate"],
),
LibraryMapping(
"quick3dassetimport",
"Qt6",
"Qt::Quick3DAssetImport",
components=["Quick3DAssetImport"],
),
LibraryMapping("core5compat", "Qt6", "Qt::Core5Compat", components=["Core5Compat"]),
LibraryMapping("quick3d", "Qt6", "Qt::Quick3D", components=["Quick3D"]),
LibraryMapping("quick3drender", "Qt6", "Qt::Quick3DRender", components=["Quick3DRender"]),
LibraryMapping(
"quick3druntimerender",
"Qt6",
"Qt::Quick3DRuntimeRender",
components=["Quick3DRuntimeRender"],
),
LibraryMapping("quick3dutils", "Qt6", "Qt::Quick3DUtils", components=["Quick3DUtils"]),
LibraryMapping("quickcontrols2", "Qt6", "Qt::QuickControls2", components=["QuickControls2"]),
LibraryMapping(
"quickcontrols2impl",
"Qt6",
"Qt::QuickControls2Impl",
components=["QuickControls2Impl"],
),
LibraryMapping("quick", "Qt6", "Qt::Quick", components=["Quick"]),
LibraryMapping(
"quickshapes", "Qt6", "Qt::QuickShapesPrivate", components=["QuickShapesPrivate"]
),
LibraryMapping("quicktemplates2", "Qt6", "Qt::QuickTemplates2", components=["QuickTemplates2"]),
LibraryMapping("quickwidgets", "Qt6", "Qt::QuickWidgets", components=["QuickWidgets"]),
LibraryMapping("remoteobjects", "Qt6", "Qt::RemoteObjects", components=["RemoteObjects"]),
LibraryMapping("script", "Qt6", "Qt::Script", components=["Script"]),
LibraryMapping("scripttools", "Qt6", "Qt::ScriptTools", components=["ScriptTools"]),
LibraryMapping("scxml", "Qt6", "Qt::Scxml", components=["Scxml"]),
LibraryMapping("sensors", "Qt6", "Qt::Sensors", components=["Sensors"]),
LibraryMapping("serialport", "Qt6", "Qt::SerialPort", components=["SerialPort"]),
LibraryMapping("serialbus", "Qt6", "Qt::SerialBus", components=["SerialBus"]),
LibraryMapping("services", "Qt6", "Qt::ServiceSupport", components=["ServiceSupport"]),
LibraryMapping("service_support", "Qt6", "Qt::ServiceSupport", components=["ServiceSupport"]),
LibraryMapping("shadertools", "Qt6", "Qt::ShaderTools", components=["ShaderTools"]),
LibraryMapping("statemachine", "Qt6", "Qt::StateMachine", components=["StateMachine"]),
LibraryMapping("sql", "Qt6", "Qt::Sql", components=["Sql"]),
LibraryMapping("svg", "Qt6", "Qt::Svg", components=["Svg"]),
LibraryMapping("svgwidgets", "Qt6", "Qt::SvgWidgets", components=["SvgWidgets"]),
LibraryMapping("charts", "Qt6", "Qt::Charts", components=["Charts"]),
LibraryMapping("testlib", "Qt6", "Qt::Test", components=["Test"]),
LibraryMapping("texttospeech", "Qt6", "Qt::TextToSpeech", components=["TextToSpeech"]),
LibraryMapping("theme_support", "Qt6", "Qt::ThemeSupport", components=["ThemeSupport"]),
LibraryMapping("tts", "Qt6", "Qt::TextToSpeech", components=["TextToSpeech"]),
LibraryMapping("uiplugin", "Qt6", "Qt::UiPlugin", components=["UiPlugin"]),
LibraryMapping("uitools", "Qt6", "Qt::UiTools", components=["UiTools"]),
LibraryMapping("virtualkeyboard", "Qt6", "Qt::VirtualKeyboard", components=["VirtualKeyboard"]),
LibraryMapping("waylandclient", "Qt6", "Qt::WaylandClient", components=["WaylandClient"]),
LibraryMapping(
"waylandcompositor",
"Qt6",
"Qt::WaylandCompositor",
components=["WaylandCompositor"],
),
LibraryMapping("webchannel", "Qt6", "Qt::WebChannel", components=["WebChannel"]),
LibraryMapping("webengine", "Qt6", "Qt::WebEngine", components=["WebEngine"]),
LibraryMapping(
"webenginewidgets", "Qt6", "Qt::WebEngineWidgets", components=["WebEngineWidgets"]
),
LibraryMapping("websockets", "Qt6", "Qt::WebSockets", components=["WebSockets"]),
LibraryMapping("webview", "Qt6", "Qt::WebView", components=["WebView"]),
LibraryMapping("widgets", "Qt6", "Qt::Widgets", components=["Widgets"]),
LibraryMapping("window-lib", "Qt6", "Qt::AppManWindow", components=["AppManWindow"]),
LibraryMapping("winextras", "Qt6", "Qt::WinExtras", components=["WinExtras"]),
LibraryMapping("x11extras", "Qt6", "Qt::X11Extras", components=["X11Extras"]),
LibraryMapping("xcb_qpa_lib", "Qt6", "Qt::XcbQpaPrivate", components=["XcbQpaPrivate"]),
LibraryMapping(
"xkbcommon_support", "Qt6", "Qt::XkbCommonSupport", components=["XkbCommonSupport"]
),
LibraryMapping("xmlpatterns", "Qt6", "Qt::XmlPatterns", components=["XmlPatterns"]),
LibraryMapping("xml", "Qt6", "Qt::Xml", components=["Xml"]),
LibraryMapping("qmlworkerscript", "Qt6", "Qt::QmlWorkerScript", components=["QmlWorkerScript"]),
LibraryMapping(
"quickparticles",
"Qt6",
"Qt::QuickParticlesPrivate",
components=["QuickParticlesPrivate"],
),
LibraryMapping(
"linuxofono_support",
"Qt6",
"Qt::LinuxOfonoSupport",
components=["LinuxOfonoSupport"],
),
LibraryMapping(
"linuxofono_support_private",
"Qt6",
"Qt::LinuxOfonoSupportPrivate",
components=["LinuxOfonoSupportPrivate"],
),
LibraryMapping("tools", "Qt6", "Qt::Tools", components=["Tools"]),
LibraryMapping("axcontainer", "Qt6", "Qt::AxContainer", components=["AxContainer"]),
LibraryMapping("webkitwidgets", "Qt6", "Qt::WebKitWidgets", components=["WebKitWidgets"]),
LibraryMapping("zlib", "Qt6", "Qt::Zlib", components=["Zlib"]),
LibraryMapping("httpserver", "Qt6", "Qt::HttpServer", components=["HttpServer"]),
LibraryMapping("sslserver", "Qt6", "Qt::SslServer", components=["HttpServer"]),
]
# Note that the library map is adjusted dynamically further down.
_library_map = [
# 3rd party:
LibraryMapping("atspi", "ATSPI2", "PkgConfig::ATSPI2"),
LibraryMapping(
"backtrace", "WrapBacktrace", "WrapBacktrace::WrapBacktrace", emit_if="config.unix"
),
LibraryMapping("bluez", "BlueZ", "PkgConfig::BlueZ"),
LibraryMapping("brotli", "WrapBrotli", "WrapBrotli::WrapBrotliDec"),
LibraryMapping("corewlan", None, None),
LibraryMapping("cups", "Cups", "Cups::Cups"),
LibraryMapping("directfb", "DirectFB", "PkgConfig::DirectFB"),
LibraryMapping("db2", "DB2", "DB2::DB2"),
LibraryMapping("dbus", "WrapDBus1", "dbus-1", resultVariable="DBus1", extra=["1.2"]),
LibraryMapping(
"doubleconversion", "WrapSystemDoubleConversion",
"WrapSystemDoubleConversion::WrapSystemDoubleConversion"
),
LibraryMapping("dlt", "DLT", "DLT::DLT"),
LibraryMapping("drm", "Libdrm", "Libdrm::Libdrm"),
LibraryMapping("egl", "EGL", "EGL::EGL"),
LibraryMapping("flite", "Flite", "Flite::Flite"),
LibraryMapping("flite_alsa", "ALSA", "ALSA::ALSA"),
LibraryMapping(
"fontconfig", "Fontconfig", "Fontconfig::Fontconfig", resultVariable="FONTCONFIG"
),
LibraryMapping(
"freetype",
"WrapFreetype",
"WrapFreetype::WrapFreetype",
extra=["2.2.0", "REQUIRED"],
is_bundled_with_qt=True,
),
LibraryMapping("gbm", "gbm", "gbm::gbm"),
LibraryMapping("glib", "GLIB2", "GLIB2::GLIB2"),
LibraryMapping("iconv", "WrapIconv", "WrapIconv::WrapIconv"),
LibraryMapping("gtk3", "GTK3", "PkgConfig::GTK3", extra=["3.6"]),
LibraryMapping("gssapi", "GSSAPI", "GSSAPI::GSSAPI"),
LibraryMapping(
"harfbuzz",
"WrapHarfbuzz",
"WrapHarfbuzz::WrapHarfbuzz",
is_bundled_with_qt=True,
extra=["2.6.0"],
),
LibraryMapping("host_dbus", None, None),
LibraryMapping("icu", "ICU", "ICU::i18n ICU::uc ICU::data", components=["i18n", "uc", "data"]),
LibraryMapping("journald", "Libsystemd", "PkgConfig::Libsystemd"),
LibraryMapping("jpeg", "JPEG", "JPEG::JPEG"), # see also libjpeg
LibraryMapping("libatomic", "WrapAtomic", "WrapAtomic::WrapAtomic"),
LibraryMapping("libb2", "Libb2", "Libb2::Libb2"),
LibraryMapping("libclang", "WrapLibClang", "WrapLibClang::WrapLibClang"),
LibraryMapping("libdl", None, "${CMAKE_DL_LIBS}"),
LibraryMapping("libinput", "Libinput", "Libinput::Libinput"),
LibraryMapping("libjpeg", "JPEG", "JPEG::JPEG"), # see also jpeg
LibraryMapping("libpng", "WrapPNG", "WrapPNG::WrapPNG", is_bundled_with_qt=True),
LibraryMapping("libproxy", "Libproxy", "PkgConfig::Libproxy"),
LibraryMapping("librt", "WrapRt", "WrapRt::WrapRt"),
LibraryMapping("libudev", "Libudev", "PkgConfig::Libudev"),
LibraryMapping("lttng-ust", "LTTngUST", "LTTng::UST", resultVariable="LTTNGUST"),
LibraryMapping("libmd4c", "WrapMd4c", "WrapMd4c::WrapMd4c", is_bundled_with_qt=True),
LibraryMapping("mtdev", "Mtdev", "PkgConfig::Mtdev"),
LibraryMapping("mysql", "MySQL", "MySQL::MySQL"),
LibraryMapping("odbc", "ODBC", "ODBC::ODBC"),
LibraryMapping("opengl_es2", "GLESv2", "GLESv2::GLESv2"),
LibraryMapping("opengl", "WrapOpenGL", "WrapOpenGL::WrapOpenGL", resultVariable="WrapOpenGL"),
LibraryMapping(
"openssl_headers",
"WrapOpenSSLHeaders",
"WrapOpenSSLHeaders::WrapOpenSSLHeaders",
resultVariable="TEST_openssl_headers",
appendFoundSuffix=False,
test_library_overwrite="WrapOpenSSLHeaders::WrapOpenSSLHeaders",
run_library_test=True,
),
LibraryMapping(
"openssl",
"WrapOpenSSL",
"WrapOpenSSL::WrapOpenSSL",
resultVariable="TEST_openssl",
appendFoundSuffix=False,
run_library_test=True,
no_link_so_name="openssl_headers",
),
LibraryMapping("oci", "Oracle", "Oracle::OCI"),
LibraryMapping(
"pcre2",
"WrapPCRE2",
"WrapPCRE2::WrapPCRE2",
extra=["10.20", "REQUIRED"],
is_bundled_with_qt=True,
),
LibraryMapping("pps", "PPS", "PPS::PPS"),
LibraryMapping("psql", "PostgreSQL", "PostgreSQL::PostgreSQL"),
LibraryMapping("slog2", "Slog2", "Slog2::Slog2"),
LibraryMapping("speechd", "SpeechDispatcher", "SpeechDispatcher::SpeechDispatcher"),
LibraryMapping("sqlite2", None, None), # No more sqlite2 support in Qt6!
LibraryMapping("sqlite3", "SQLite3", "SQLite::SQLite3"),
LibraryMapping("sqlite", "SQLite3", "SQLite::SQLite3"),
LibraryMapping(
"taglib", "WrapTagLib", "WrapTagLib::WrapTagLib", is_bundled_with_qt=True
), # used in qtivi
LibraryMapping("tslib", "Tslib", "PkgConfig::Tslib"),
LibraryMapping("udev", "Libudev", "PkgConfig::Libudev"),
LibraryMapping("udev", "Libudev", "PkgConfig::Libudev"), # see also libudev!
LibraryMapping("vulkan", "WrapVulkanHeaders", "WrapVulkanHeaders::WrapVulkanHeaders"),
LibraryMapping("wayland_server", "Wayland", "Wayland::Server"), # used in qtbase/src/gui
LibraryMapping("wayland-server", "Wayland", "Wayland::Server"), # used in qtwayland
LibraryMapping("wayland-client", "Wayland", "Wayland::Client"),
LibraryMapping("wayland-cursor", "Wayland", "Wayland::Cursor"),
LibraryMapping("wayland-egl", "Wayland", "Wayland::Egl"),
LibraryMapping(
"wayland-kms", "Waylandkms", "PkgConfig::Waylandkms"
), # TODO: check if this actually works
LibraryMapping("x11", "X11", "X11::X11"),
LibraryMapping("x11sm", "X11", "${X11_SM_LIB} ${X11_ICE_LIB}", resultVariable="X11_SM"),
LibraryMapping(
"xcb",
"XCB",
"XCB::XCB",
extra=["1.11"],
resultVariable="TARGET XCB::XCB",
appendFoundSuffix=False,
),
LibraryMapping("xcb_glx", "XCB", "XCB::GLX", components=["GLX"], resultVariable="XCB_GLX"),
LibraryMapping(
"xcb_cursor",
"XCB",
"XCB::CURSOR",
extra=["0.1.1", "COMPONENTS", "CURSOR"],
resultVariable="XCB_CURSOR",
),
LibraryMapping(
"xcb_icccm",
"XCB",
"XCB::ICCCM",
extra=["0.3.9"],
components=["ICCCM"],
resultVariable="XCB_ICCCM",
),
LibraryMapping(
"xcb_image",
"XCB",
"XCB::IMAGE",
extra=["0.3.9"],
components=["IMAGE"],
resultVariable="XCB_IMAGE",
),
LibraryMapping(
"xcb_keysyms",
"XCB",
"XCB::KEYSYMS",
extra=["0.3.9"],
components=["KEYSYMS"],
resultVariable="XCB_KEYSYMS",
),
LibraryMapping(
"xcb_randr", "XCB", "XCB::RANDR", components=["RANDR"], resultVariable="XCB_RANDR"
),
LibraryMapping(
"xcb_render",
"XCB",
"XCB::RENDER",
components=["RENDER"],
resultVariable="XCB_RENDER",
),
LibraryMapping(
"xcb_renderutil",
"XCB",
"XCB::RENDERUTIL",
extra=["0.3.9"],
components=["RENDERUTIL"],
resultVariable="XCB_RENDERUTIL",
),
LibraryMapping(
"xcb_shape", "XCB", "XCB::SHAPE", components=["SHAPE"], resultVariable="XCB_SHAPE"
),
LibraryMapping("xcb_shm", "XCB", "XCB::SHM", components=["SHM"], resultVariable="XCB_SHM"),
LibraryMapping("xcb_sync", "XCB", "XCB::SYNC", components=["SYNC"], resultVariable="XCB_SYNC"),
LibraryMapping(
"xcb_xfixes",
"XCB",
"XCB::XFIXES",
components=["XFIXES"],
resultVariable="TARGET XCB::XFIXES",
appendFoundSuffix=False,
),
LibraryMapping(
"xcb-xfixes",
"XCB",
"XCB::XFIXES",
components=["XFIXES"],
resultVariable="TARGET XCB::XFIXES",
appendFoundSuffix=False,
),
LibraryMapping(
"xcb_xinput",
"XCB",
"XCB::XINPUT",
extra=["1.12"],
components=["XINPUT"],
resultVariable="XCB_XINPUT",
),
LibraryMapping("xcb_xkb", "XCB", "XCB::XKB", components=["XKB"], resultVariable="XCB_XKB"),
LibraryMapping("xcb_xlib", "X11_XCB", "X11::XCB"),
LibraryMapping("xcomposite", "XComposite", "PkgConfig::XComposite"),
LibraryMapping("xkbcommon_evdev", "XKB", "XKB::XKB", extra=["0.5.0"]), # see also xkbcommon
LibraryMapping("xkbcommon_x11", "XKB_COMMON_X11", "PkgConfig::XKB_COMMON_X11", extra=["0.5.0"]),
LibraryMapping("xkbcommon", "XKB", "XKB::XKB", extra=["0.5.0"]),
LibraryMapping("xlib", "X11", "X11::X11"),
LibraryMapping("xrender", "XRender", "PkgConfig::XRender", extra=["0.6"]),
LibraryMapping("zlib", "WrapZLIB", "WrapZLIB::WrapZLIB", extra=["1.0.8"]),
LibraryMapping("zstd", "WrapZSTD", "WrapZSTD::WrapZSTD", extra=["1.3"]),
LibraryMapping("tiff", "TIFF", "TIFF::TIFF"),
LibraryMapping("webp", "WrapWebP", "WrapWebP::WrapWebP"),
LibraryMapping("jasper", "WrapJasper", "WrapJasper::WrapJasper"),
LibraryMapping("mng", "Libmng", "Libmng::Libmng"),
LibraryMapping("sdl2", "WrapSDL2", "WrapSDL2::WrapSDL2"),
LibraryMapping("hunspell", "Hunspell", "Hunspell::Hunspell"),
LibraryMapping(
"qt3d-assimp",
"WrapQt3DAssimp",
"WrapQt3DAssimp::WrapQt3DAssimp",
extra=["5"],
run_library_test=True,
resultVariable="TEST_assimp",
appendFoundSuffix=False,
),
LibraryMapping(
"quick3d_assimp",
"WrapQuick3DAssimp",
"WrapQuick3DAssimp::WrapQuick3DAssimp",
extra=["5"],
run_library_test=True,
resultVariable="TEST_quick3d_assimp",
appendFoundSuffix=False,
),
]
def _adjust_library_map():
# Assign a Linux condition on all wayland related packages.
# Assign platforms that have X11 condition on all X11 related packages.
# We don't want to get pages of package not found messages on
# Windows and macOS, and this also improves configure time on
# those platforms.
linux_package_prefixes = ["wayland"]
x11_package_prefixes = ["xcb", "x11", "xkb", "xrender", "xlib"]
for i, _ in enumerate(_library_map):
if any([_library_map[i].soName.startswith(p) for p in linux_package_prefixes]):
_library_map[i].emit_if = "config.linux"
if any([_library_map[i].soName.startswith(p) for p in x11_package_prefixes]):
_library_map[i].emit_if = "X11_SUPPORTED"
_adjust_library_map()
def find_3rd_party_library_mapping(soName: str) -> typing.Optional[LibraryMapping]:
for i in _library_map:
if i.soName == soName:
return i
return None
def find_qt_library_mapping(soName: str) -> typing.Optional[LibraryMapping]:
for i in _qt_library_map:
if i.soName == soName:
return i
return None
def find_library_info_for_target(targetName: str) -> typing.Optional[LibraryMapping]:
qt_target = targetName
if targetName.endswith("Private"):
qt_target = qt_target[:-7]
for i in _qt_library_map:
if i.targetName == qt_target:
return i
for i in _library_map:
if i.targetName == targetName:
return i
return None
# For a given qmake library (e.g. 'openssl_headers'), check whether this is a fake library used
# for the /nolink annotation, and return the actual annotated qmake library ('openssl/nolink').
def find_annotated_qmake_lib_name(lib: str) -> str:
for entry in _library_map:
if entry.no_link_so_name == lib:
return entry.soName + "/nolink"
return lib
def featureName(name: str) -> str:
replacement_char = "_"
if name.startswith("c++"):
replacement_char = "x"
return re.sub(r"[^a-zA-Z0-9_]", replacement_char, name)
def map_qt_library(lib: str) -> str:
private = False
if lib.endswith("-private"):
private = True
lib = lib[:-8]
mapped = find_qt_library_mapping(lib)
qt_name = lib
if mapped:
assert mapped.targetName # Qt libs must have a target name set
qt_name = mapped.targetName
if private:
qt_name += "Private"
return qt_name
platform_mapping = {
"win32": "WIN32",
"win": "WIN32",
"unix": "UNIX",
"darwin": "APPLE",
"linux": "LINUX",
"integrity": "INTEGRITY",
"qnx": "QNX",
"vxworks": "VXWORKS",
"hpux": "HPUX",
"nacl": "NACL",
"android": "ANDROID",
"uikit": "UIKIT",
"tvos": "TVOS",
"watchos": "WATCHOS",
"winrt": "WINRT",
"wasm": "WASM",
"emscripten": "EMSCRIPTEN",
"msvc": "MSVC",
"clang": "CLANG",
"gcc": "GCC",
"icc": "ICC",
"intel_icc": "ICC",
"osx": "MACOS",
"ios": "IOS",
"freebsd": "FREEBSD",
"openbsd": "OPENBSD",
"mingw": "MINGW",
"netbsd": "NETBSD",
"haiku": "HAIKU",
"mac": "APPLE",
"macx": "MACOS",
"macos": "MACOS",
"macx-icc": "(MACOS AND ICC)",
}
def map_platform(platform: str) -> str:
"""Return the qmake platform as cmake platform or the unchanged string."""
return platform_mapping.get(platform, platform)
def is_known_3rd_party_library(lib: str) -> bool:
handling_no_link = False
if lib.endswith("/nolink") or lib.endswith("_nolink"):
lib = lib[:-7]
handling_no_link = True
mapping = find_3rd_party_library_mapping(lib)
if handling_no_link and mapping and mapping.no_link_so_name:
no_link_mapping = find_3rd_party_library_mapping(mapping.no_link_so_name)
if no_link_mapping:
mapping = no_link_mapping
return mapping is not None
def map_3rd_party_library(lib: str) -> str:
handling_no_link = False
libpostfix = ""
if lib.endswith("/nolink"):
lib = lib[:-7]
libpostfix = "_nolink"
handling_no_link = True
mapping = find_3rd_party_library_mapping(lib)
if handling_no_link and mapping and mapping.no_link_so_name:
no_link_mapping = find_3rd_party_library_mapping(mapping.no_link_so_name)
if no_link_mapping:
mapping = no_link_mapping
libpostfix = ""
if not mapping or not mapping.targetName:
return lib
return mapping.targetName + libpostfix
compile_test_dependent_library_mapping = {
"dtls": {"openssl": "openssl_headers"},
"ocsp": {"openssl": "openssl_headers"},
}
def get_compile_test_dependent_library_mapping(compile_test_name: str, dependency_name: str):
if compile_test_name in compile_test_dependent_library_mapping:
mapping = compile_test_dependent_library_mapping[compile_test_name]
if dependency_name in mapping:
return mapping[dependency_name]
return dependency_name
def generate_find_package_info(
lib: LibraryMapping,
use_qt_find_package: bool = True,
*,
indent: int = 0,
emit_if: str = "",
use_system_package_name: bool = False,
remove_REQUIRED_from_extra: bool = True,
components_required: bool = True,
module: str = "",
) -> str:
isRequired = False
extra = lib.extra.copy()
if lib.components:
extra.append("COMPONENTS" if components_required else "OPTIONAL_COMPONENTS")
extra += lib.components
if "REQUIRED" in extra and use_qt_find_package:
isRequired = True
if remove_REQUIRED_from_extra:
extra.remove("REQUIRED")
cmake_target_name = lib.targetName
assert cmake_target_name
# _nolink or not does not matter at this point:
if cmake_target_name.endswith("_nolink") or cmake_target_name.endswith("/nolink"):
cmake_target_name = cmake_target_name[:-7]
initial_package_name: str = lib.packageName if lib.packageName else ""
package_name: str = initial_package_name
if use_system_package_name:
replace_args = ["Wrap", "WrapSystem"]
package_name = package_name.replace(*replace_args) # type: ignore
cmake_target_name = cmake_target_name.replace(*replace_args) # type: ignore
if use_qt_find_package:
if cmake_target_name:
extra += ["PROVIDED_TARGETS", cmake_target_name]
if module:
extra += ["MODULE_NAME", module]
extra += ["QMAKE_LIB", find_annotated_qmake_lib_name(lib.soName)]
result = ""
one_ind = " "
ind = one_ind * indent
if use_qt_find_package:
if extra:
result = f"{ind}qt_find_package({package_name} {' '.join(extra)})\n"
else:
result = f"{ind}qt_find_package({package_name})\n"
if isRequired:
result += (
f"{ind}set_package_properties({initial_package_name} PROPERTIES TYPE REQUIRED)\n"
)
else:
if extra:
result = f"{ind}find_package({package_name} {' '.join(extra)})\n"
else:
result = f"{ind}find_package({package_name})\n"
# If a package should be found only in certain conditions, wrap
# the find_package call within that condition.
if emit_if:
result = f"if(({emit_if}) OR QT_FIND_ALL_PACKAGES_ALWAYS)\n{one_ind}{result}endif()\n"
return result
def _set_up_py_parsing_nicer_debug_output(pp):
indent = -1
def increase_indent(fn):
def wrapper_function(*args):
nonlocal indent
indent += 1
print("> " * indent, end="")
return fn(*args)
return wrapper_function
def decrease_indent(fn):
def wrapper_function(*args):
nonlocal indent
print("> " * indent, end="")
indent -= 1
return fn(*args)
return wrapper_function
if hasattr(pp, "_defaultStartDebugAction"):
pp._defaultStartDebugAction = increase_indent(pp._defaultStartDebugAction)
pp._defaultSuccessDebugAction = decrease_indent(pp._defaultSuccessDebugAction)
pp._defaultExceptionDebugAction = decrease_indent(pp._defaultExceptionDebugAction)
elif hasattr(pp.core, "_default_start_debug_action"):
pp.core._default_start_debug_action = increase_indent(pp.core._default_start_debug_action)
pp.core._default_success_debug_action = decrease_indent(
pp.core._default_success_debug_action
)
pp.core._default_exception_debug_action = decrease_indent(
pp.core._default_exception_debug_action
)

76
util/cmake/json_parser.py Normal file
View File

@ -0,0 +1,76 @@
#!/usr/bin/env python3
# Copyright (C) 2019 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import pyparsing as pp # type: ignore
import json
import re
from helper import _set_up_py_parsing_nicer_debug_output
_set_up_py_parsing_nicer_debug_output(pp)
class QMakeSpecificJSONParser:
def __init__(self, *, debug: bool = False) -> None:
self.debug = debug
self.grammar = self.create_py_parsing_grammar()
def create_py_parsing_grammar(self):
# Keep around all whitespace.
pp.ParserElement.setDefaultWhitespaceChars("")
def add_element(name: str, value: pp.ParserElement):
nonlocal self
if self.debug:
value.setName(name)
value.setDebug()
return value
# Our grammar is pretty simple. We want to remove all newlines
# inside quoted strings, to make the quoted strings JSON
# compliant. So our grammar should skip to the first quote while
# keeping everything before it as-is, process the quoted string
# skip to the next quote, and repeat that until the end of the
# file.
EOF = add_element("EOF", pp.StringEnd())
SkipToQuote = add_element("SkipToQuote", pp.SkipTo('"'))
SkipToEOF = add_element("SkipToEOF", pp.SkipTo(EOF))
def remove_newlines_and_whitespace_in_quoted_string(tokens):
first_string = tokens[0]
replaced_string = re.sub(r"\n[ ]*", " ", first_string)
return replaced_string
QuotedString = add_element(
"QuotedString", pp.QuotedString(quoteChar='"', multiline=True, unquoteResults=False)
)
QuotedString.setParseAction(remove_newlines_and_whitespace_in_quoted_string)
QuotedTerm = add_element("QuotedTerm", pp.Optional(SkipToQuote) + QuotedString)
Grammar = add_element("Grammar", pp.OneOrMore(QuotedTerm) + SkipToEOF)
return Grammar
def parse_file_using_py_parsing(self, file: str):
print(f'Pre processing "{file}" using py parsing to remove incorrect newlines.')
try:
with open(file, "r") as file_fd:
contents = file_fd.read()
parser_result = self.grammar.parseString(contents, parseAll=True)
token_list = parser_result.asList()
joined_string = "".join(token_list)
return joined_string
except pp.ParseException as pe:
print(pe.line)
print(" " * (pe.col - 1) + "^")
print(pe)
raise pe
def parse(self, file: str):
pre_processed_string = self.parse_file_using_py_parsing(file)
print(f'Parsing "{file}" using json.loads().')
json_parsed = json.loads(pre_processed_string)
return json_parsed

5102
util/cmake/pro2cmake.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,210 @@
#!/usr/bin/env python3
# Copyright (C) 2019 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from __future__ import annotations
"""
This utility script shows statistics about
converted .pro -> CMakeLists.txt files.
To execute: python3 pro_conversion_rate.py <src dir>
where <src dir> can be any qt source directory. For better statistics,
specify a module root source dir (like ./qtbase or ./qtsvg).
"""
from argparse import ArgumentParser
import os
import typing
from typing import Dict, Union
from timeit import default_timer
def _parse_commandline():
parser = ArgumentParser(description="Find pro files for which there are no CMakeLists.txt.")
parser.add_argument(
"source_directory", metavar="<src dir>", type=str, help="The source directory"
)
return parser.parse_args()
class Blacklist:
"""Class to check if a certain dir_name / dir_path is blacklisted"""
def __init__(self, names: typing.List[str], path_parts: typing.List[str]):
self.names = names
self.path_parts = path_parts
# The lookup algorithm
self.lookup = self.is_blacklisted_part
self.tree = None
try:
# If package is available, use Aho-Corasick algorithm,
from ahocorapy.keywordtree import KeywordTree # type: ignore
self.tree = KeywordTree(case_insensitive=True)
for p in self.path_parts:
self.tree.add(p)
self.tree.finalize()
self.lookup = self.is_blacklisted_part_aho
except ImportError:
pass
def is_blacklisted(self, dir_name: str, dir_path: str) -> bool:
# First check if exact dir name is blacklisted.
if dir_name in self.names:
return True
# Check if a path part is blacklisted (e.g. util/cmake)
return self.lookup(dir_path)
def is_blacklisted_part(self, dir_path: str) -> bool:
if any(part in dir_path for part in self.path_parts):
return True
return False
def is_blacklisted_part_aho(self, dir_path: str) -> bool:
return self.tree.search(dir_path) is not None # type: ignore
def recursive_scan(path: str, extension: str, result_paths: typing.List[str], blacklist: Blacklist):
"""Find files ending with a certain extension, filtering out blacklisted entries"""
try:
for entry in os.scandir(path):
if entry.is_file() and entry.path.endswith(extension):
result_paths.append(entry.path)
elif entry.is_dir():
if blacklist.is_blacklisted(entry.name, entry.path):
continue
recursive_scan(entry.path, extension, result_paths, blacklist)
except Exception as e:
print(e)
def check_for_cmake_project(pro_path: str) -> bool:
pro_dir_name = os.path.dirname(pro_path)
cmake_project_path = os.path.join(pro_dir_name, "CMakeLists.txt")
return os.path.exists(cmake_project_path)
def compute_stats(
src_path: str,
pros_with_missing_project: typing.List[str],
total_pros: int,
existing_pros: int,
missing_pros: int,
) -> dict:
stats: Dict[str, Dict[str, Union[str, int, float]]] = {}
stats["total projects"] = {"label": "Total pro files found", "value": total_pros}
stats["existing projects"] = {
"label": "Existing CMakeLists.txt files found",
"value": existing_pros,
}
stats["missing projects"] = {
"label": "Missing CMakeLists.txt files found",
"value": missing_pros,
}
stats["missing examples"] = {"label": "Missing examples", "value": 0}
stats["missing tests"] = {"label": "Missing tests", "value": 0}
stats["missing src"] = {"label": "Missing src/**/**", "value": 0}
stats["missing plugins"] = {"label": "Missing plugins", "value": 0}
for p in pros_with_missing_project:
rel_path = os.path.relpath(p, src_path)
if rel_path.startswith("examples"):
assert isinstance(stats["missing examples"]["value"], int)
stats["missing examples"]["value"] += 1
elif rel_path.startswith("tests"):
assert isinstance(stats["missing tests"]["value"], int)
stats["missing tests"]["value"] += 1
elif rel_path.startswith(os.path.join("src", "plugins")):
assert isinstance(stats["missing plugins"]["value"], int)
stats["missing plugins"]["value"] += 1
elif rel_path.startswith("src"):
assert isinstance(stats["missing src"]["value"], int)
stats["missing src"]["value"] += 1
for stat in stats:
if int(stats[stat]["value"]) > 0:
stats[stat]["percentage"] = round(float(stats[stat]["value"]) * 100 / total_pros, 2)
return stats
def print_stats(
src_path: str,
pros_with_missing_project: typing.List[str],
stats: dict,
scan_time: float,
script_time: float,
):
if stats["total projects"]["value"] == 0:
print("No .pro files found. Did you specify a correct source path?")
return
if stats["total projects"]["value"] == stats["existing projects"]["value"]:
print("All projects were converted.")
else:
print("Missing CMakeLists.txt files for the following projects: \n")
for p in pros_with_missing_project:
rel_path = os.path.relpath(p, src_path)
print(rel_path)
print("\nStatistics: \n")
for stat in stats:
if stats[stat]["value"] > 0:
print(
f"{stats[stat]['label']:<40}: {stats[stat]['value']} ({stats[stat]['percentage']}%)"
)
print(f"\n{'Scan time':<40}: {scan_time:.10f} seconds")
print(f"{'Total script time':<40}: {script_time:.10f} seconds")
def main():
args = _parse_commandline()
src_path = os.path.abspath(args.source_directory)
pro_paths = []
extension = ".pro"
blacklist_names = ["config.tests", "doc", "3rdparty", "angle"]
blacklist_path_parts = [os.path.join("util", "cmake")]
script_start_time = default_timer()
blacklist = Blacklist(blacklist_names, blacklist_path_parts)
scan_time_start = default_timer()
recursive_scan(src_path, extension, pro_paths, blacklist)
scan_time_end = default_timer()
scan_time = scan_time_end - scan_time_start
total_pros = len(pro_paths)
pros_with_missing_project = []
for pro_path in pro_paths:
if not check_for_cmake_project(pro_path):
pros_with_missing_project.append(pro_path)
missing_pros = len(pros_with_missing_project)
existing_pros = total_pros - missing_pros
stats = compute_stats(
src_path, pros_with_missing_project, total_pros, existing_pros, missing_pros
)
script_end_time = default_timer()
script_time = script_end_time - script_start_time
print_stats(src_path, pros_with_missing_project, stats, scan_time, script_time)
if __name__ == "__main__":
main()

422
util/cmake/qmake_parser.py Normal file
View File

@ -0,0 +1,422 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import collections
import os
import re
from itertools import chain
from typing import Tuple
import pyparsing as pp # type: ignore
from helper import _set_up_py_parsing_nicer_debug_output
_set_up_py_parsing_nicer_debug_output(pp)
def fixup_linecontinuation(contents: str) -> str:
# Remove all line continuations, aka a backslash followed by
# a newline character with an arbitrary amount of whitespace
# between the backslash and the newline.
# This greatly simplifies the qmake parsing grammar.
contents = re.sub(r"([^\t ])\\[ \t]*\n", "\\1 ", contents)
contents = re.sub(r"\\[ \t]*\n", "", contents)
return contents
def fixup_comments(contents: str) -> str:
# Get rid of completely commented out lines.
# So any line which starts with a '#' char and ends with a new line
# will be replaced by a single new line.
# The # may be preceded by any number of spaces or tabs.
#
# This is needed because qmake syntax is weird. In a multi line
# assignment (separated by backslashes and newlines aka
# # \\\n ), if any of the lines are completely commented out, in
# principle the assignment should fail.
#
# It should fail because you would have a new line separating
# the previous value from the next value, and the next value would
# not be interpreted as a value, but as a new token / operation.
# qmake is lenient though, and accepts that, so we need to take
# care of it as well, as if the commented line didn't exist in the
# first place.
contents = re.sub(r"(^|\n)[ \t]*#[^\n]*?\n", "\n", contents, re.DOTALL)
return contents
def flatten_list(input_list):
"""Flattens an irregular nested list into a simple list."""
for el in input_list:
if isinstance(el, collections.abc.Iterable) and not isinstance(el, (str, bytes)):
yield from flatten_list(el)
else:
yield el
def handle_function_value(group: pp.ParseResults):
function_name = group[0]
function_args = group[1]
if function_name == "qtLibraryTarget":
if len(function_args) > 1:
raise RuntimeError(
"Don't know what to with more than one function argument "
"for $$qtLibraryTarget()."
)
return str(function_args[0])
if function_name == "quote":
# Do nothing, just return a string result
return str(group)
if function_name == "files":
return str(function_args[0])
if function_name == "basename":
if len(function_args) != 1:
print(f"XXXX basename with more than one argument")
if function_args[0] == "_PRO_FILE_PWD_":
return os.path.basename(os.getcwd())
print(f"XXXX basename with value other than _PRO_FILE_PWD_")
return os.path.basename(str(function_args[0]))
if isinstance(function_args, pp.ParseResults):
function_args = list(flatten_list(function_args.asList()))
# For other functions, return the whole expression as a string.
return f"$${function_name}({' '.join(function_args)})"
class QmakeParser:
def __init__(self, *, debug: bool = False) -> None:
self.debug = debug
self._Grammar = self._generate_grammar()
def _generate_grammar(self):
# Define grammar:
pp.ParserElement.setDefaultWhitespaceChars(" \t")
def add_element(name: str, value: pp.ParserElement):
nonlocal self
if self.debug:
value.setName(name)
value.setDebug()
return value
EOL = add_element("EOL", pp.Suppress(pp.LineEnd()))
Else = add_element("Else", pp.Keyword("else"))
Identifier = add_element(
"Identifier", pp.Word(f"{pp.alphas}_", bodyChars=pp.alphanums + "_-./")
)
BracedValue = add_element(
"BracedValue",
pp.nestedExpr(
ignoreExpr=pp.quotedString
| pp.QuotedString(
quoteChar="$(", endQuoteChar=")", escQuote="\\", unquoteResults=False
)
).setParseAction(lambda s, l, t: ["(", *t[0], ")"]),
)
Substitution = add_element(
"Substitution",
pp.Combine(
pp.Literal("$")
+ (
(
(pp.Literal("$") + Identifier + pp.Optional(pp.nestedExpr()))
| (pp.Literal("(") + Identifier + pp.Literal(")"))
| (pp.Literal("{") + Identifier + pp.Literal("}"))
| (
pp.Literal("$")
+ pp.Literal("{")
+ Identifier
+ pp.Optional(pp.nestedExpr())
+ pp.Literal("}")
)
| (pp.Literal("$") + pp.Literal("[") + Identifier + pp.Literal("]"))
)
)
),
)
LiteralValuePart = add_element(
"LiteralValuePart", pp.Word(pp.printables, excludeChars="$#{}()")
)
SubstitutionValue = add_element(
"SubstitutionValue",
pp.Combine(pp.OneOrMore(Substitution | LiteralValuePart | pp.Literal("$"))),
)
FunctionValue = add_element(
"FunctionValue",
pp.Group(
pp.Suppress(pp.Literal("$") + pp.Literal("$"))
+ Identifier
+ pp.nestedExpr() # .setParseAction(lambda s, l, t: ['(', *t[0], ')'])
).setParseAction(lambda s, l, t: handle_function_value(*t)),
)
Value = add_element(
"Value",
pp.NotAny(Else | pp.Literal("}") | EOL)
+ (
pp.QuotedString(quoteChar='"', escChar="\\")
| FunctionValue
| SubstitutionValue
| BracedValue
),
)
Values = add_element("Values", pp.ZeroOrMore(Value)("value"))
Op = add_element(
"OP",
pp.Literal("=")
| pp.Literal("-=")
| pp.Literal("+=")
| pp.Literal("*=")
| pp.Literal("~="),
)
Key = add_element("Key", Identifier)
Operation = add_element(
"Operation", Key("key") + pp.locatedExpr(Op)("operation") + Values("value")
)
CallArgs = add_element("CallArgs", pp.nestedExpr())
def parse_call_args(results):
out = ""
for item in chain(*results):
if isinstance(item, str):
out += item
else:
out += "(" + parse_call_args(item) + ")"
return out
CallArgs.setParseAction(parse_call_args)
Load = add_element("Load", pp.Keyword("load") + CallArgs("loaded"))
Include = add_element(
"Include", pp.Keyword("include") + pp.locatedExpr(CallArgs)("included")
)
Option = add_element("Option", pp.Keyword("option") + CallArgs("option"))
RequiresCondition = add_element("RequiresCondition", pp.originalTextFor(pp.nestedExpr()))
def parse_requires_condition(s, l_unused, t):
# The following expression unwraps the condition via the additional info
# set by originalTextFor.
condition_without_parentheses = s[t._original_start + 1 : t._original_end - 1]
# And this replaces the colons with '&&' similar how it's done for 'Condition'.
condition_without_parentheses = (
condition_without_parentheses.strip().replace(":", " && ").strip(" && ")
)
return condition_without_parentheses
RequiresCondition.setParseAction(parse_requires_condition)
Requires = add_element(
"Requires", pp.Keyword("requires") + RequiresCondition("project_required_condition")
)
FunctionArgumentsAsString = add_element(
"FunctionArgumentsAsString", pp.originalTextFor(pp.nestedExpr())
)
QtNoMakeTools = add_element(
"QtNoMakeTools",
pp.Keyword("qtNomakeTools") + FunctionArgumentsAsString("qt_no_make_tools_arguments"),
)
# ignore the whole thing...
DefineTestDefinition = add_element(
"DefineTestDefinition",
pp.Suppress(
pp.Keyword("defineTest")
+ CallArgs
+ pp.nestedExpr(opener="{", closer="}", ignoreExpr=pp.LineEnd())
),
)
# ignore the whole thing...
ForLoop = add_element(
"ForLoop",
pp.Suppress(
pp.Keyword("for")
+ CallArgs
+ pp.nestedExpr(opener="{", closer="}", ignoreExpr=pp.LineEnd())
),
)
# ignore the whole thing...
ForLoopSingleLine = add_element(
"ForLoopSingleLine",
pp.Suppress(pp.Keyword("for") + CallArgs + pp.Literal(":") + pp.SkipTo(EOL)),
)
# ignore the whole thing...
FunctionCall = add_element("FunctionCall", pp.Suppress(Identifier + pp.nestedExpr()))
Scope = add_element("Scope", pp.Forward())
Statement = add_element(
"Statement",
pp.Group(
Load
| Include
| Option
| Requires
| QtNoMakeTools
| ForLoop
| ForLoopSingleLine
| DefineTestDefinition
| FunctionCall
| Operation
),
)
StatementLine = add_element("StatementLine", Statement + (EOL | pp.FollowedBy("}")))
StatementGroup = add_element(
"StatementGroup", pp.ZeroOrMore(StatementLine | Scope | pp.Suppress(EOL))
)
Block = add_element(
"Block",
pp.Suppress("{")
+ pp.Optional(EOL)
+ StatementGroup
+ pp.Optional(EOL)
+ pp.Suppress("}")
+ pp.Optional(EOL),
)
ConditionEnd = add_element(
"ConditionEnd",
pp.FollowedBy(
(pp.Optional(pp.White()) + (pp.Literal(":") | pp.Literal("{") | pp.Literal("|")))
),
)
ConditionPart1 = add_element(
"ConditionPart1", (pp.Optional("!") + Identifier + pp.Optional(BracedValue))
)
ConditionPart2 = add_element("ConditionPart2", pp.CharsNotIn("#{}|:=\\\n"))
ConditionPart = add_element(
"ConditionPart", (ConditionPart1 ^ ConditionPart2) + ConditionEnd
)
ConditionOp = add_element("ConditionOp", pp.Literal("|") ^ pp.Literal(":"))
ConditionWhiteSpace = add_element(
"ConditionWhiteSpace", pp.Suppress(pp.Optional(pp.White(" ")))
)
# Unfortunately qmake condition operators have no precedence,
# and are simply evaluated left to right. To emulate that, wrap
# each condition sub-expression in parentheses.
# So c1|c2:c3 is evaluated by qmake as (c1|c2):c3.
# The following variable keeps count on how many parentheses
# should be added to the beginning of the condition. Each
# condition sub-expression always gets an ")", and in the
# end the whole condition gets many "(". Note that instead
# inserting the actual parentheses, we insert special markers
# which get replaced in the end.
condition_parts_count = 0
# Whitespace in the markers is important. Assumes the markers
# never appear in .pro files.
l_paren_marker = "_(_ "
r_paren_marker = " _)_"
def handle_condition_part(condition_part_parse_result: pp.ParseResults) -> str:
condition_part_list = [*condition_part_parse_result]
nonlocal condition_parts_count
condition_parts_count += 1
condition_part_joined = "".join(condition_part_list)
# Add ending parenthesis marker. The counterpart is added
# in handle_condition.
return f"{condition_part_joined}{r_paren_marker}"
ConditionPart.setParseAction(handle_condition_part)
ConditionRepeated = add_element(
"ConditionRepeated", pp.ZeroOrMore(ConditionOp + ConditionWhiteSpace + ConditionPart)
)
def handle_condition(condition_parse_results: pp.ParseResults) -> str:
nonlocal condition_parts_count
prepended_parentheses = l_paren_marker * condition_parts_count
result = prepended_parentheses + " ".join(condition_parse_results).strip().replace(
":", " && "
).strip(" && ")
# If there are only 2 condition sub-expressions, there is no
# need for parentheses.
if condition_parts_count < 3:
result = result.replace(l_paren_marker, "")
result = result.replace(r_paren_marker, "")
result = result.strip(" ")
else:
result = result.replace(l_paren_marker, "( ")
result = result.replace(r_paren_marker, " )")
# Strip parentheses and spaces around the final
# condition.
result = result[1:-1]
result = result.strip(" ")
# Reset the parenthesis count for the next condition.
condition_parts_count = 0
return result
Condition = add_element("Condition", pp.Combine(ConditionPart + ConditionRepeated))
Condition.setParseAction(handle_condition)
# Weird thing like write_file(a)|error() where error() is the alternative condition
# which happens to be a function call. In this case there is no scope, but our code expects
# a scope with a list of statements, so create a fake empty statement.
ConditionEndingInFunctionCall = add_element(
"ConditionEndingInFunctionCall",
pp.Suppress(ConditionOp)
+ FunctionCall
+ pp.Empty().setParseAction(lambda x: [[]]).setResultsName("statements"),
)
SingleLineScope = add_element(
"SingleLineScope",
pp.Suppress(pp.Literal(":")) + pp.Group(Block | (Statement + EOL))("statements"),
)
MultiLineScope = add_element("MultiLineScope", Block("statements"))
SingleLineElse = add_element(
"SingleLineElse",
pp.Suppress(pp.Literal(":")) + (Scope | Block | (Statement + pp.Optional(EOL))),
)
MultiLineElse = add_element("MultiLineElse", Block)
ElseBranch = add_element("ElseBranch", pp.Suppress(Else) + (SingleLineElse | MultiLineElse))
# Scope is already add_element'ed in the forward declaration above.
Scope <<= pp.Group(
Condition("condition")
+ (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall)
+ pp.Optional(ElseBranch)("else_statements")
)
Grammar = StatementGroup("statements")
Grammar.ignore(pp.pythonStyleComment())
return Grammar
def parseFile(self, file: str) -> Tuple[pp.ParseResults, str]:
print(f'Parsing "{file}"...')
try:
with open(file, "r") as file_fd:
contents = file_fd.read()
# old_contents = contents
contents = fixup_comments(contents)
contents = fixup_linecontinuation(contents)
result = self._Grammar.parseString(contents, parseAll=True)
except pp.ParseException as pe:
print(pe.line)
print(f"{' ' * (pe.col-1)}^")
print(pe)
raise pe
return result, contents
def parseProFile(file: str, *, debug=False) -> Tuple[pp.ParseResults, str]:
parser = QmakeParser(debug=debug)
return parser.parseFile(file)

View File

@ -0,0 +1,8 @@
pytest; python_version >= '3.7'
pytest-cov; python_version >= '3.7'
mypy; python_version >= '3.7'
pyparsing; python_version >= '3.7'
sympy; python_version >= '3.7'
portalocker; python_version >= '3.7'
black; python_version >= '3.7'

221
util/cmake/run_pro2cmake.py Normal file
View File

@ -0,0 +1,221 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import glob
import os
import subprocess
import concurrent.futures
import sys
import typing
import argparse
from argparse import ArgumentParser
def parse_command_line() -> argparse.Namespace:
parser = ArgumentParser(
description="Run pro2cmake on all .pro files recursively in given path. "
"You can pass additional arguments to the pro2cmake calls by appending "
"-- --foo --bar"
)
parser.add_argument(
"--only-existing",
dest="only_existing",
action="store_true",
help="Run pro2cmake only on .pro files that already have a CMakeLists.txt.",
)
parser.add_argument(
"--only-missing",
dest="only_missing",
action="store_true",
help="Run pro2cmake only on .pro files that do not have a CMakeLists.txt.",
)
parser.add_argument(
"--only-qtbase-main-modules",
dest="only_qtbase_main_modules",
action="store_true",
help="Run pro2cmake only on the main modules in qtbase.",
)
parser.add_argument(
"--skip-subdirs-projects",
dest="skip_subdirs_projects",
action="store_true",
help="Don't run pro2cmake on TEMPLATE=subdirs projects.",
)
parser.add_argument(
"--is-example",
dest="is_example",
action="store_true",
help="Run pro2cmake with --is-example flag.",
)
parser.add_argument(
"--count", dest="count", help="How many projects should be converted.", type=int
)
parser.add_argument(
"--offset",
dest="offset",
help="From the list of found projects, from which project should conversion begin.",
type=int,
)
parser.add_argument(
"path", metavar="<path>", type=str, help="The path where to look for .pro files."
)
args, unknown = parser.parse_known_args()
# Error out when the unknown arguments do not start with a "--",
# which implies passing through arguments to pro2cmake.
if len(unknown) > 0 and unknown[0] != "--":
parser.error("unrecognized arguments: {}".format(" ".join(unknown)))
else:
args.pro2cmake_args = unknown[1:]
return args
def find_all_pro_files(base_path: str, args: argparse.Namespace):
def sorter(pro_file: str) -> str:
"""Sorter that tries to prioritize main pro files in a directory."""
pro_file_without_suffix = pro_file.rsplit("/", 1)[-1][:-4]
dir_name = os.path.dirname(pro_file)
if dir_name == ".":
dir_name = os.path.basename(os.getcwd())
if dir_name.endswith(pro_file_without_suffix):
return dir_name
return dir_name + "/__" + pro_file
all_files = []
previous_dir_name: typing.Optional[str] = None
print("Finding .pro files.")
glob_result = glob.glob(os.path.join(base_path, "**/*.pro"), recursive=True)
def cmake_lists_exists_filter(path):
path_dir_name = os.path.dirname(path)
if os.path.exists(os.path.join(path_dir_name, "CMakeLists.txt")):
return True
return False
def cmake_lists_missing_filter(path):
return not cmake_lists_exists_filter(path)
def qtbase_main_modules_filter(path):
main_modules = [
"corelib",
"network",
"gui",
"widgets",
"testlib",
"printsupport",
"opengl",
"sql",
"dbus",
"concurrent",
"xml",
]
path_suffixes = [f"src/{m}/{m}.pro" for m in main_modules]
for path_suffix in path_suffixes:
if path.endswith(path_suffix):
return True
return False
filter_result = glob_result
filter_func = None
if args.only_existing:
filter_func = cmake_lists_exists_filter
elif args.only_missing:
filter_func = cmake_lists_missing_filter
elif args.only_qtbase_main_modules:
filter_func = qtbase_main_modules_filter
if filter_func:
print("Filtering.")
filter_result = [p for p in filter_result if filter_func(p)]
for pro_file in sorted(filter_result, key=sorter):
dir_name = os.path.dirname(pro_file)
if dir_name == previous_dir_name:
print("Skipping:", pro_file)
else:
all_files.append(pro_file)
previous_dir_name = dir_name
return all_files
def run(all_files: typing.List[str], pro2cmake: str, args: argparse.Namespace) -> typing.List[str]:
failed_files = []
files_count = len(all_files)
workers = os.cpu_count() or 1
if args.only_qtbase_main_modules:
# qtbase main modules take longer than usual to process.
workers = 2
with concurrent.futures.ThreadPoolExecutor(max_workers=workers, initargs=(10,)) as pool:
print("Firing up thread pool executor.")
def _process_a_file(data: typing.Tuple[str, int, int]) -> typing.Tuple[int, str, str]:
filename, index, total = data
pro2cmake_args = []
if sys.platform == "win32":
pro2cmake_args.append(sys.executable)
pro2cmake_args.append(pro2cmake)
if args.is_example:
pro2cmake_args.append("--is-example")
if args.skip_subdirs_projects:
pro2cmake_args.append("--skip-subdirs-project")
pro2cmake_args.append(os.path.basename(filename))
if args.pro2cmake_args:
pro2cmake_args += args.pro2cmake_args
result = subprocess.run(
pro2cmake_args,
cwd=os.path.dirname(filename),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
stdout = f"Converted[{index}/{total}]: {filename}\n"
return result.returncode, filename, stdout + result.stdout.decode()
for return_code, filename, stdout in pool.map(
_process_a_file,
zip(all_files, range(1, files_count + 1), (files_count for _ in all_files)),
):
if return_code:
failed_files.append(filename)
print(stdout)
return failed_files
def main() -> None:
args = parse_command_line()
script_path = os.path.dirname(os.path.abspath(__file__))
pro2cmake = os.path.join(script_path, "pro2cmake.py")
base_path = args.path
all_files = find_all_pro_files(base_path, args)
if args.offset:
all_files = all_files[args.offset :]
if args.count:
all_files = all_files[: args.count]
files_count = len(all_files)
failed_files = run(all_files, pro2cmake, args)
if len(all_files) == 0:
print("No files found.")
if failed_files:
print(
f"The following files were not successfully "
f"converted ({len(failed_files)} of {files_count}):"
)
for f in failed_files:
print(f' "{f}"')
if __name__ == "__main__":
main()

View File

@ -0,0 +1,397 @@
#!/usr/bin/env python3
# Copyright (C) 2019 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
"""
This is a helper script that takes care of reapplying special case
modifications when regenerating a CMakeLists.txt file using
pro2cmake.py or configure.cmake with configurejson2cmake.py.
It has two modes of operation:
1) Dumb "special case" block removal and re-application.
2) Smart "special case" diff application, using a previously generated
"clean" CMakeLists.txt/configure.cmake as a source. "clean" in this
case means a generated file which has no "special case" modifications.
Both modes use a temporary git repository to compute and reapply
"special case" diffs.
For the first mode to work, the developer has to mark changes
with "# special case" markers on every line they want to keep. Or
enclose blocks of code they want to keep between "# special case begin"
and "# special case end" markers.
For example:
SOURCES
foo.cpp
bar.cpp # special case
SOURCES
foo1.cpp
foo2.cpp
# special case begin
foo3.cpp
foo4.cpp
# special case end
The second mode, as mentioned, requires a previous "clean"
CMakeLists.txt/configure.cmake file.
The script can then compute the exact diff between
a "clean" and "modified" (with special cases) file, and reapply that
diff to a newly generated "CMakeLists.txt"/"configure.cmake" file.
This implies that we always have to keep a "clean" file alongside the
"modified" project file for each project (corelib, gui, etc.) So we
have to commit both files to the repository.
If there is no such "clean" file, we can use the first operation mode
to generate one. After that, we only have to use the second operation
mode for the project file in question.
When the script is used, the developer only has to take care of fixing
the newly generated "modified" file. The "clean" file is automatically
handled and git add'ed by the script, and will be committed together
with the "modified" file.
"""
import re
import os
import subprocess
import filecmp
import time
import typing
import stat
from shutil import copyfile
from shutil import rmtree
from textwrap import dedent
def remove_special_cases(original: str) -> str:
# Remove content between the following markers
# '# special case begin' and '# special case end'.
# This also remove the markers.
replaced = re.sub(
r"\n[^#\n]*?#[^\n]*?special case begin.*?#[^\n]*special case end[^\n]*?\n",
"\n",
original,
0,
re.DOTALL,
)
# Remove individual lines that have the "# special case" marker.
replaced = re.sub(r"\n.*#.*special case[^\n]*\n", "\n", replaced)
return replaced
def read_content_from_file(file_path: str) -> str:
with open(file_path, "r") as file_fd:
content = file_fd.read()
return content
def write_content_to_file(file_path: str, content: str) -> None:
with open(file_path, "w") as file_fd:
file_fd.write(content)
def resolve_simple_git_conflicts(file_path: str, debug=False) -> None:
content = read_content_from_file(file_path)
# If the conflict represents the addition of a new content hunk,
# keep the content and remove the conflict markers.
if debug:
print("Resolving simple conflicts automatically.")
replaced = re.sub(r"\n<<<<<<< HEAD\n=======(.+?)>>>>>>> master\n", r"\1", content, 0, re.DOTALL)
write_content_to_file(file_path, replaced)
def copyfile_log(src: str, dst: str, debug=False):
if debug:
print(f"Copying {src} to {dst}.")
copyfile(src, dst)
def check_if_git_in_path() -> bool:
is_win = os.name == "nt"
for path in os.environ["PATH"].split(os.pathsep):
git_path = os.path.join(path, "git")
if is_win:
git_path += ".exe"
if os.path.isfile(git_path) and os.access(git_path, os.X_OK):
return True
return False
def run_process_quiet(args_string: str, debug=False) -> bool:
if debug:
print(f'Running command: "{args_string}"')
args_list = args_string.split()
try:
subprocess.run(args_list, check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
# git merge with conflicts returns with exit code 1, but that's not
# an error for us.
if "git merge" not in args_string:
if debug:
print(
dedent(
f"""\
Error while running: "{args_string}"
{e.stdout}"""
)
)
return False
return True
def does_file_have_conflict_markers(file_path: str, debug=False) -> bool:
if debug:
print(f"Checking if {file_path} has no leftover conflict markers.")
content_actual = read_content_from_file(file_path)
if "<<<<<<< HEAD" in content_actual:
print(f"Conflict markers found in {file_path}. " "Please remove or solve them first.")
return True
return False
def create_file_with_no_special_cases(
original_file_path: str, no_special_cases_file_path: str, debug=False
):
"""
Reads content of original CMakeLists.txt/configure.cmake, removes all content
between "# special case" markers or lines, saves the result into a
new file.
"""
content_actual = read_content_from_file(original_file_path)
if debug:
print(f"Removing special case blocks from {original_file_path}.")
content_no_special_cases = remove_special_cases(content_actual)
if debug:
print(
f"Saving original contents of {original_file_path} "
f"with removed special case blocks to {no_special_cases_file_path}"
)
write_content_to_file(no_special_cases_file_path, content_no_special_cases)
def rm_tree_on_error_handler(func: typing.Callable[..., None], path: str, exception_info: tuple):
# If the path is read only, try to make it writable, and try
# to remove the path again.
if not os.access(path, os.W_OK):
os.chmod(path, stat.S_IWRITE)
func(path)
else:
print(f"Error while trying to remove path: {path}. Exception: {exception_info}")
class SpecialCaseHandler(object):
def __init__(
self,
original_file_path: str,
generated_file_path: str,
base_dir: str,
keep_temporary_files=False,
debug=False,
) -> None:
self.base_dir = base_dir
self.original_file_path = original_file_path
self.generated_file_path = generated_file_path
self.keep_temporary_files = keep_temporary_files
self.use_heuristic = False
self.debug = debug
@property
def prev_file_path(self) -> str:
filename = ".prev_" + os.path.basename(self.original_file_path)
return os.path.join(self.base_dir, filename)
@property
def post_merge_file_path(self) -> str:
original_file_name = os.path.basename(self.original_file_path)
(original_file_basename, original_file_ext) = os.path.splitext(original_file_name)
filename = original_file_basename + "-post-merge" + original_file_ext
return os.path.join(self.base_dir, filename)
@property
def no_special_file_path(self) -> str:
original_file_name = os.path.basename(self.original_file_path)
(original_file_basename, original_file_ext) = os.path.splitext(original_file_name)
filename = original_file_basename + ".no-special" + original_file_ext
return os.path.join(self.base_dir, filename)
def apply_git_merge_magic(self, no_special_cases_file_path: str) -> None:
# Create new folder for temporary repo, and ch dir into it.
repo = os.path.join(self.base_dir, "tmp_repo")
repo_absolute_path = os.path.abspath(repo)
txt = os.path.basename(self.original_file_path)
try:
os.mkdir(repo)
current_dir = os.getcwd()
os.chdir(repo)
except Exception as e:
print(f"Failed to create temporary directory for temporary git repo. Exception: {e}")
raise e
generated_file_path = os.path.join("..", self.generated_file_path)
original_file_path = os.path.join("..", self.original_file_path)
no_special_cases_file_path = os.path.join("..", no_special_cases_file_path)
post_merge_file_path = os.path.join("..", self.post_merge_file_path)
try:
# Create new repo with the "clean" CMakeLists.txt/configure.cmake file.
run_process_quiet("git init .", debug=self.debug)
run_process_quiet("git config user.name fake", debug=self.debug)
run_process_quiet("git config user.email fake@fake", debug=self.debug)
copyfile_log(no_special_cases_file_path, txt, debug=self.debug)
run_process_quiet(f"git add {txt}", debug=self.debug)
run_process_quiet("git commit -m no_special", debug=self.debug)
run_process_quiet("git checkout -b no_special", debug=self.debug)
# Copy the original "modified" file (with the special cases)
# and make a new commit.
run_process_quiet("git checkout -b original", debug=self.debug)
copyfile_log(original_file_path, txt, debug=self.debug)
run_process_quiet(f"git add {txt}", debug=self.debug)
run_process_quiet("git commit -m original", debug=self.debug)
# Checkout the commit with "clean" file again, and create a
# new branch.
run_process_quiet("git checkout no_special", debug=self.debug)
run_process_quiet("git checkout -b newly_generated", debug=self.debug)
# Copy the new "modified" file and make a commit.
copyfile_log(generated_file_path, txt, debug=self.debug)
run_process_quiet(f"git add {txt}", debug=self.debug)
run_process_quiet("git commit -m newly_generated", debug=self.debug)
# Merge the "old" branch with modifications into the "new"
# branch with the newly generated file.
run_process_quiet("git merge original", debug=self.debug)
# Resolve some simple conflicts (just remove the markers)
# for cases that don't need intervention.
resolve_simple_git_conflicts(txt, debug=self.debug)
# Copy the resulting file from the merge.
copyfile_log(txt, post_merge_file_path)
except Exception as e:
print(f"Git merge conflict resolution process failed. Exception: {e}")
raise e
finally:
os.chdir(current_dir)
# Remove the temporary repo.
try:
if not self.keep_temporary_files:
rmtree(repo_absolute_path, onerror=rm_tree_on_error_handler)
except Exception as e:
print(f"Error removing temporary repo. Exception: {e}")
def save_next_clean_file(self):
files_are_equivalent = filecmp.cmp(self.generated_file_path, self.post_merge_file_path)
if not files_are_equivalent:
# Before overriding the generated file with the post
# merge result, save the new "clean" file for future
# regenerations.
copyfile_log(self.generated_file_path, self.prev_file_path, debug=self.debug)
# Attempt to git add until we succeed. It can fail when
# run_pro2cmake executes pro2cmake in multiple threads, and git
# has acquired the index lock.
success = False
failed_once = False
i = 0
while not success and i < 20:
success = run_process_quiet(f"git add {self.prev_file_path}", debug=self.debug)
if not success:
failed_once = True
i += 1
time.sleep(0.1)
if failed_once and not success:
if self.debug:
print("Retrying git add, the index.lock was probably acquired.")
if failed_once and success:
if self.debug:
print("git add succeeded.")
elif failed_once and not success:
print(f"git add failed. Make sure to git add {self.prev_file_path} yourself.")
def handle_special_cases_helper(self) -> bool:
"""
Uses git to reapply special case modifications to the "new"
generated CMakeLists.gen.txt/configure.cmake.gen file.
If use_heuristic is True, a new file is created from the
original file, with special cases removed.
If use_heuristic is False, an existing "clean" file with no
special cases is used from a previous conversion. The "clean"
file is expected to be in the same folder as the original one.
"""
try:
if does_file_have_conflict_markers(self.original_file_path):
return False
if self.use_heuristic:
create_file_with_no_special_cases(
self.original_file_path, self.no_special_file_path
)
no_special_cases_file_path = self.no_special_file_path
else:
no_special_cases_file_path = self.prev_file_path
if self.debug:
print(
f"Using git to reapply special case modifications to newly "
f"generated {self.generated_file_path} file"
)
self.apply_git_merge_magic(no_special_cases_file_path)
self.save_next_clean_file()
copyfile_log(self.post_merge_file_path, self.generated_file_path)
if not self.keep_temporary_files:
os.remove(self.post_merge_file_path)
if self.debug:
print(
"Special case reapplication using git is complete. "
"Make sure to fix remaining conflict markers."
)
except Exception as e:
print(f"Error occurred while trying to reapply special case modifications: {e}")
return False
finally:
if not self.keep_temporary_files and self.use_heuristic:
os.remove(self.no_special_file_path)
return True
def handle_special_cases(self) -> bool:
original_file_exists = os.path.isfile(self.original_file_path)
prev_file_exists = os.path.isfile(self.prev_file_path)
self.use_heuristic = not prev_file_exists
git_available = check_if_git_in_path()
keep_special_cases = original_file_exists and git_available
if not git_available:
print(
"You need to have git in PATH in order to reapply the special "
"case modifications."
)
copy_generated_file = True
if keep_special_cases:
copy_generated_file = self.handle_special_cases_helper()
return copy_generated_file

View File

View File

@ -0,0 +1,6 @@
# QtCore can't be compiled with -Wl,-no-undefined because it uses the "environ"
# variable and on FreeBSD and OpenBSD, this variable is in the final executable itself.
# OpenBSD 6.0 will include environ in libc.
freebsd|openbsd: QMAKE_LFLAGS_NOUNDEF =
include(animation/animation.pri)

View File

@ -0,0 +1,2 @@
qmake-clean.commands += (cd qmake && $(MAKE) clean ":-(==)-:" '(Foo)' )

View File

@ -0,0 +1,4 @@
!system("dbus-send --session --type=signal / local.AutotestCheck.Hello >$$QMAKE_SYSTEM_NULL_DEVICE 2>&1") {
SOURCES = dbus.cpp
}

View File

@ -0,0 +1,22 @@
linux:!static {
precompile_header {
# we'll get an error if we just use SOURCES +=
no_pch_assembler.commands = $$QMAKE_CC -c $(CFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
no_pch_assembler.dependency_type = TYPE_C
no_pch_assembler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}
no_pch_assembler.input = NO_PCH_ASM
no_pch_assembler.name = compiling[no_pch] ${QMAKE_FILE_IN}
silent: no_pch_assembler.commands = @echo compiling[no_pch] ${QMAKE_FILE_IN} && $$no_pch_assembler.commands
CMAKE_ANGLE_GLES2_IMPLIB_RELEASE = libGLESv2.$${QMAKE_EXTENSION_STATICLIB}
HOST_BINS = $$[QT_HOST_BINS]
CMAKE_HOST_DATA_DIR = $$[QT_HOST_DATA/src]/
TR_EXCLUDE += ../3rdparty/*
QMAKE_EXTRA_COMPILERS += no_pch_assembler
NO_PCH_ASM += global/minimum-linux.S
} else {
SOURCES += global/minimum-linux.S
}
HEADERS += global/minimum-linux_p.h
}

View File

@ -0,0 +1,11 @@
a1|a2 {
DEFINES += d
}
b1|b2:b3 {
DEFINES += d
}
c1|c2:c3|c4 {
DEFINES += d
}

View File

@ -0,0 +1,2 @@
write_file("a", contents)|error()

View File

@ -0,0 +1,4 @@
contains(DEFINES,QT_EVAL):include(eval.pri)
HOST_BINS = $$[QT_HOST_BINS]

View File

@ -0,0 +1,4 @@
TARGET = myapp
QT = core network widgets
win32: QT += opengl
SOURCES = main.cpp

View File

@ -0,0 +1,8 @@
QT += core gui
SOURCES += main.cpp
greaterThan(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 1):equals(QT_PATCH_VERSION, 0) {
DEFINES += SUPER_FRESH_MAJOR_QT_RELEASE
}
greaterThan(QT_VERSION, 6.6.5):lessThan(QT_VERSION, 6.6.7):equals(QT_VERSION, 6.6.6): {
DEFINES += QT_VERSION_OF_THE_BEAST
}

View File

@ -0,0 +1,3 @@
TARGET = myapp
QT = core network widgets
SOURCES = main.cpp

View File

@ -0,0 +1,6 @@
defineTest(pathIsAbsolute) {
p = $$clean_path($$1)
!isEmpty(p):isEqual(p, $$absolute_path($$p)): return(true)
return(false)
}

View File

@ -0,0 +1,6 @@
linux {
SOURCES += a.cpp
} else {
SOURCES += b.cpp
}

View File

@ -0,0 +1,4 @@
osx: A = 1
else: win32: B = 2
else: C = 3

View File

@ -0,0 +1,7 @@
qtConfig(timezone) {
A = 1
} else:win32 {
B = 2
} else {
C = 3
}

View File

@ -0,0 +1,6 @@
qtConfig(timezone) {
A = 1
} else:win32: B = 2
else {
C = 3
}

View File

@ -0,0 +1,10 @@
# comments
qtConfig(timezone) { # bar
A = 1
} else:win32 {
B = 2 # foo
} else { C = 3
# baz
# foobar
}
# endcomment

View File

@ -0,0 +1,11 @@
qtConfig(timezone) \
{
A = \
1
} \
else:win32: \
B = 2
else: \
C \
= 3

View File

@ -0,0 +1,2 @@
msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x65000000

View File

@ -0,0 +1,5 @@
qtConfig(timezone) { A = 1 } else:win32: {\
B = 2 \
} else: \
C \
= 3 \

View File

@ -0,0 +1,2 @@
MODULE_AUX_INCLUDES = \
\$\$QT_MODULE_INCLUDE_BASE/QtANGLE

View File

@ -0,0 +1,11 @@
SOURCES = main.cpp
for (config, SIMD) {
uc = $$upper($$config)
DEFINES += QT_COMPILER_SUPPORTS_$${uc}
add_cflags {
cflags = QMAKE_CFLAGS_$${uc}
!defined($$cflags, var): error("This compiler does not support $${uc}")
QMAKE_CXXFLAGS += $$eval($$cflags)
}
}

View File

@ -0,0 +1,4 @@
pathIsAbsolute($$CMAKE_HOST_DATA_DIR) {
CMAKE_HOST_DATA_DIR = $$[QT_HOST_DATA/src]/
}

View File

@ -0,0 +1,3 @@
A = 42
include(foo) # load foo
B=23

View File

@ -0,0 +1,10 @@
TEMPLATE=subdirs
SUBDIRS=\
qmacstyle \
qstyle \
qstyleoption \
qstylesheetstyle \
!qtConfig(private_tests): SUBDIRS -= \
qstylesheetstyle \

View File

@ -0,0 +1,22 @@
SUBDIRS = \
# dds \
tga \
wbmp
MYVAR = foo # comment
MYVAR = foo2# comment
MYVAR = foo3# comment #
MYVAR = foo4# comment #
##
#
#
##
#
#
#
# #
MYVAR = foo5# comment # #

View File

@ -0,0 +1,3 @@
A = 42
load(foo)# load foo
B=23

View File

@ -0,0 +1,3 @@
equals(a): \
greaterThan(a):flags += 1

View File

@ -0,0 +1,4 @@
A = 42 \
43 \
44
B=23

View File

@ -0,0 +1,2 @@
requires(qtConfig(dlopen))

View File

@ -0,0 +1,5 @@
if(linux*|hurd*):!cross_compile:!static:!*-armcc* {
prog=$$quote(if (/program interpreter: (.*)]/) { print $1; })
DEFINES += ELF_INTERPRETER=\\\"$$system(LC_ALL=C readelf -l /bin/ls | perl -n -e \'$$prog\')\\\"
}

View File

@ -0,0 +1,4 @@
for(d, sd): \
exists($$d/$${d}.pro): \
SUBDIRS += $$d

View File

@ -0,0 +1,3 @@
TEMPLATE = subdirs
SUBDIRS = \
kernel \

View File

@ -0,0 +1,17 @@
win32 {
!winrt {
SOURCES +=io/qstandardpaths_win.cpp
} else {
SOURCES +=io/qstandardpaths_winrt.cpp
}
} else:unix {
mac {
OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm
} else:android {
SOURCES += io/qstandardpaths_android.cpp
} else:haiku {
SOURCES += io/qstandardpaths_haiku.cpp
} else {
SOURCES += io/qstandardpaths_unix.cpp
}
}

View File

@ -0,0 +1,2 @@
unset(f16c_cxx)

View File

@ -0,0 +1,2 @@
TARGET = Dummy
TARGET = $$qtLibraryTarget($$TARGET)

View File

@ -0,0 +1,66 @@
#!/usr/bin/env python3
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from pro2cmake import Scope, SetOperation, merge_scopes, recursive_evaluate_scope
from tempfile import TemporaryDirectory
import os
import pathlib
import pytest
import re
import shutil
import subprocess
import tempfile
import typing
debug_mode = bool(os.environ.get("DEBUG_PRO2CMAKE_TEST_CONVERSION"))
test_script_dir = pathlib.Path(__file__).parent.resolve()
pro2cmake_dir = test_script_dir.parent.resolve()
pro2cmake_py = pro2cmake_dir.joinpath("pro2cmake.py")
test_data_dir = test_script_dir.joinpath("data", "conversion")
def convert(base_name: str):
pro_file_name = str(base_name) + ".pro"
pro_file_path = test_data_dir.joinpath(pro_file_name)
assert(pro_file_path.exists())
with TemporaryDirectory(prefix="testqmake2cmake") as tmp_dir_str:
tmp_dir = pathlib.Path(tmp_dir_str)
output_file_path = tmp_dir.joinpath("CMakeLists.txt")
exit_code = subprocess.call([pro2cmake_py, "--is-example", "-o", output_file_path, pro_file_path])
assert(exit_code == 0)
if debug_mode:
shutil.copyfile(output_file_path, tempfile.gettempdir() + "/pro2cmake/CMakeLists.txt")
f = open(output_file_path, "r")
assert(f)
content = f.read()
assert(content)
return content
def test_qt_modules():
output = convert("required_qt_modules")
find_package_lines = []
for line in output.split("\n"):
if "find_package(" in line:
find_package_lines.append(line.strip())
assert(["find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)",
"find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Network Widgets)"] == find_package_lines)
output = convert("optional_qt_modules")
find_package_lines = []
for line in output.split("\n"):
if "find_package(" in line:
find_package_lines.append(line.strip())
assert(["find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)",
"find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Network Widgets)",
"find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS OpenGL)"] == find_package_lines)
def test_qt_version_check():
output = convert("qt_version_check")
interesting_lines = []
for line in output.split("\n"):
if line.startswith("if(") and "QT_VERSION" in line:
interesting_lines.append(line.strip())
assert(["if(( ( (QT_VERSION_MAJOR GREATER 5) ) AND (QT_VERSION_MINOR LESS 1) ) AND (QT_VERSION_PATCH EQUAL 0))", "if(( ( (QT_VERSION VERSION_GREATER 6.6.5) ) AND (QT_VERSION VERSION_LESS 6.6.7) ) AND (QT_VERSION VERSION_EQUAL 6.6.6))"] == interesting_lines)

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from qmake_parser import fixup_linecontinuation
def test_no_change():
input = "test \\\nline2\n line3"
output = "test line2\n line3"
result = fixup_linecontinuation(input)
assert output == result
def test_fix():
input = "test \\\t\nline2\\\n line3\\ \nline4 \\ \t\nline5\\\n\n\n"
output = "test line2 line3 line4 line5 \n\n"
result = fixup_linecontinuation(input)
assert output == result

View File

@ -0,0 +1,160 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from condition_simplifier import simplify_condition
def validate_simplify(input: str, expected: str) -> None:
output = simplify_condition(input)
assert output == expected
def validate_simplify_unchanged(input: str) -> None:
validate_simplify(input, input)
def test_simplify_on():
validate_simplify_unchanged('ON')
def test_simplify_off():
validate_simplify_unchanged('OFF')
def test_simplify_not_on():
validate_simplify('NOT ON', 'OFF')
def test_simplify_not_off():
validate_simplify('NOT OFF', 'ON')
def test_simplify_isEmpty():
validate_simplify_unchanged('isEmpty(foo)')
def test_simplify_not_isEmpty():
validate_simplify_unchanged('NOT isEmpty(foo)')
def test_simplify_simple_and():
validate_simplify_unchanged('QT_FEATURE_bar AND QT_FEATURE_foo')
def test_simplify_simple_or():
validate_simplify_unchanged('QT_FEATURE_bar OR QT_FEATURE_foo')
def test_simplify_simple_not():
validate_simplify_unchanged('NOT QT_FEATURE_foo')
def test_simplify_simple_and_reorder():
validate_simplify('QT_FEATURE_foo AND QT_FEATURE_bar', 'QT_FEATURE_bar AND QT_FEATURE_foo')
def test_simplify_simple_or_reorder():
validate_simplify('QT_FEATURE_foo OR QT_FEATURE_bar', 'QT_FEATURE_bar OR QT_FEATURE_foo')
def test_simplify_unix_or_win32():
validate_simplify('WIN32 OR UNIX', 'ON')
def test_simplify_unix_or_win32_or_foobar_or_barfoo():
validate_simplify('WIN32 OR UNIX OR foobar OR barfoo', 'ON')
def test_simplify_not_not_bar():
validate_simplify(' NOT NOT bar ', 'bar')
def test_simplify_not_unix():
validate_simplify('NOT UNIX', 'WIN32')
def test_simplify_not_win32():
validate_simplify('NOT WIN32', 'UNIX')
def test_simplify_unix_and_win32():
validate_simplify('WIN32 AND UNIX', 'OFF')
def test_simplify_unix_or_win32():
validate_simplify('WIN32 OR UNIX', 'ON')
def test_simplify_unix_and_win32_or_foobar_or_barfoo():
validate_simplify('WIN32 AND foobar AND UNIX AND barfoo', 'OFF')
def test_simplify_watchos_and_win32():
validate_simplify('WATCHOS AND WIN32', 'OFF')
def test_simplify_win32_and_watchos():
validate_simplify('WIN32 AND WATCHOS', 'OFF')
def test_simplify_apple_and_appleosx():
validate_simplify('APPLE AND MACOS', 'MACOS')
def test_simplify_apple_or_appleosx():
validate_simplify('APPLE OR MACOS', 'APPLE')
def test_simplify_apple_or_appleosx_level1():
validate_simplify('foobar AND (APPLE OR MACOS )', 'APPLE AND foobar')
def test_simplify_apple_or_appleosx_level1_double():
validate_simplify('foobar AND (APPLE OR MACOS )', 'APPLE AND foobar')
def test_simplify_apple_or_appleosx_level1_double_with_extra_spaces():
validate_simplify('foobar AND (APPLE OR MACOS ) '
'AND ( MACOS OR APPLE )', 'APPLE AND foobar')
def test_simplify_apple_or_appleosx_level2():
validate_simplify('foobar AND ( ( APPLE OR WATCHOS ) '
'OR MACOS ) AND ( MACOS OR APPLE ) '
'AND ( (WIN32 OR WINRT) OR UNIX) ', 'APPLE AND foobar')
def test_simplify_not_apple_and_appleosx():
validate_simplify('NOT APPLE AND MACOS', 'OFF')
def test_simplify_unix_and_bar_or_win32():
validate_simplify('WIN32 AND bar AND UNIX', 'OFF')
def test_simplify_unix_or_bar_or_win32():
validate_simplify('WIN32 OR bar OR UNIX', 'ON')
def test_simplify_complex_true():
validate_simplify('WIN32 OR ( APPLE OR UNIX)', 'ON')
def test_simplify_apple_unix_freebsd():
validate_simplify('( APPLE OR ( UNIX OR FREEBSD ))', 'UNIX')
def test_simplify_apple_unix_freebsd_foobar():
validate_simplify('( APPLE OR ( UNIX OR FREEBSD ) OR foobar)',
'UNIX OR foobar')
def test_simplify_complex_false():
validate_simplify('WIN32 AND foobar AND ( '
'APPLE OR ( UNIX OR FREEBSD ))',
'OFF')
def test_simplify_android_not_apple():
validate_simplify('ANDROID AND NOT MACOS', 'ANDROID')

View File

@ -0,0 +1,32 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from pro2cmake import AddOperation, SetOperation, UniqueAddOperation, RemoveOperation
def test_add_operation():
op = AddOperation(['bar', 'buz'])
result = op.process(['foo', 'bar'], ['foo', 'bar'], lambda x: x)
assert ['foo', 'bar', 'bar', 'buz'] == result
def test_uniqueadd_operation():
op = UniqueAddOperation(['bar', 'buz'])
result = op.process(['foo', 'bar'], ['foo', 'bar'], lambda x: x)
assert ['foo', 'bar', 'buz'] == result
def test_set_operation():
op = SetOperation(['bar', 'buz'])
result = op.process(['foo', 'bar'], ['foo', 'bar'], lambda x: x)
assert ['bar', 'buz'] == result
def test_remove_operation():
op = RemoveOperation(['bar', 'buz'])
result = op.process(['foo', 'bar'], ['foo', 'bar'], lambda x: x)
assert ['foo', '-buz'] == result

View File

@ -0,0 +1,343 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import os
from pro2cmake import map_condition
from qmake_parser import QmakeParser
from condition_simplifier import simplify_condition
_tests_path = os.path.dirname(os.path.abspath(__file__))
def validate_op(key, op, value, to_validate):
assert key == to_validate['key']
assert op == to_validate['operation']['value']
assert value == to_validate.get('value', None)
def validate_single_op(key, op, value, to_validate):
assert len(to_validate) == 1
validate_op(key, op, value, to_validate[0])
def evaluate_condition(to_validate):
assert 'condition' in to_validate
assert 'statements' in to_validate
return (to_validate['condition'],
to_validate['statements'],
to_validate.get('else_statements', {}))
def validate_default_else_test(file_name):
result = parse_file(file_name)
assert len(result) == 1
(cond, if_branch, else_branch) = evaluate_condition(result[0])
assert cond == 'qtConfig(timezone)'
validate_single_op('A', '=', ['1'], if_branch)
assert len(else_branch) == 1
(cond2, if2_branch, else2_branch) = evaluate_condition(else_branch[0])
assert cond2 == 'win32'
validate_single_op('B', '=', ['2'], if2_branch)
validate_single_op('C', '=', ['3'], else2_branch)
def parse_file(file):
p = QmakeParser(debug=True)
result, _ = p.parseFile(file)
print('\n\n#### Parser result:')
print(result)
print('\n#### End of parser result.\n')
print('\n\n####Parser result dictionary:')
print(result.asDict())
print('\n#### End of parser result dictionary.\n')
result_dictionary = result.asDict()
assert len(result_dictionary) == 1
return result_dictionary['statements']
def test_else():
result = parse_file(_tests_path + '/data/else.pro')
assert len(result) == 1
(cond, if_branch, else_branch) = evaluate_condition(result[0])
assert cond == 'linux'
validate_single_op('SOURCES', '+=', ['a.cpp'], if_branch)
validate_single_op('SOURCES', '+=', ['b.cpp'], else_branch)
def test_else2():
result = parse_file(_tests_path + '/data/else2.pro')
assert len(result) == 1
(cond, if_branch, else_branch) = evaluate_condition(result[0])
assert cond == 'osx'
validate_single_op('A', '=', ['1'], if_branch)
assert len(else_branch) == 1
(cond2, if2_branch, else2_branch) = evaluate_condition(else_branch[0])
assert cond2 == 'win32'
validate_single_op('B', '=', ['2'], if2_branch)
validate_single_op('C', '=', ['3'], else2_branch)
def test_else3():
validate_default_else_test(_tests_path + '/data/else3.pro')
def test_else4():
validate_default_else_test(_tests_path + '/data/else4.pro')
def test_else5():
validate_default_else_test(_tests_path + '/data/else5.pro')
def test_else6():
validate_default_else_test(_tests_path + '/data/else6.pro')
def test_else7():
result = parse_file(_tests_path + '/data/else7.pro')
assert len(result) == 1
def test_else8():
validate_default_else_test(_tests_path + '/data/else8.pro')
def test_multiline_assign():
result = parse_file(_tests_path + '/data/multiline_assign.pro')
assert len(result) == 2
validate_op('A', '=', ['42', '43', '44'], result[0])
validate_op('B', '=', ['23'], result[1])
def test_include():
result = parse_file(_tests_path + '/data/include.pro')
assert len(result) == 3
validate_op('A', '=', ['42'], result[0])
include = result[1]
assert len(include) == 1
assert 'included' in include
assert include['included'].get('value', '') == 'foo'
validate_op('B', '=', ['23'], result[2])
def test_load():
result = parse_file(_tests_path + '/data/load.pro')
assert len(result) == 3
validate_op('A', '=', ['42'], result[0])
load = result[1]
assert len(load) == 1
assert load.get('loaded', '') == 'foo'
validate_op('B', '=', ['23'], result[2])
def test_definetest():
result = parse_file(_tests_path + '/data/definetest.pro')
assert len(result) == 1
assert result[0] == []
def test_for():
result = parse_file(_tests_path + '/data/for.pro')
assert len(result) == 2
validate_op('SOURCES', '=', ['main.cpp'], result[0])
assert result[1] == []
def test_single_line_for():
result = parse_file(_tests_path + '/data/single_line_for.pro')
assert len(result) == 1
assert result[0] == []
def test_unset():
result = parse_file(_tests_path + '/data/unset.pro')
assert len(result) == 1
assert result[0] == []
def test_quoted():
result = parse_file(_tests_path + '/data/quoted.pro')
assert len(result) == 1
def test_complex_values():
result = parse_file(_tests_path + '/data/complex_values.pro')
assert len(result) == 1
def test_function_if():
result = parse_file(_tests_path + '/data/function_if.pro')
assert len(result) == 1
def test_realworld_standardpaths():
result = parse_file(_tests_path + '/data/standardpaths.pro')
(cond, if_branch, else_branch) = evaluate_condition(result[0])
assert cond == 'win32'
assert len(if_branch) == 1
assert len(else_branch) == 1
# win32:
(cond1, if_branch1, else_branch1) = evaluate_condition(if_branch[0])
assert cond1 == '!winrt'
assert len(if_branch1) == 1
validate_op('SOURCES', '+=', ['io/qstandardpaths_win.cpp'], if_branch1[0])
assert len(else_branch1) == 1
validate_op('SOURCES', '+=', ['io/qstandardpaths_winrt.cpp'], else_branch1[0])
# unix:
(cond2, if_branch2, else_branch2) = evaluate_condition(else_branch[0])
assert cond2 == 'unix'
assert len(if_branch2) == 1
assert len(else_branch2) == 0
# mac / else:
(cond3, if_branch3, else_branch3) = evaluate_condition(if_branch2[0])
assert cond3 == 'mac'
assert len(if_branch3) == 1
validate_op('OBJECTIVE_SOURCES', '+=', ['io/qstandardpaths_mac.mm'], if_branch3[0])
assert len(else_branch3) == 1
# android / else:
(cond4, if_branch4, else_branch4) = evaluate_condition(else_branch3[0])
assert cond4 == 'android'
assert len(if_branch4) == 1
validate_op('SOURCES', '+=', ['io/qstandardpaths_android.cpp'], if_branch4[0])
assert len(else_branch4) == 1
# haiku / else:
(cond5, if_branch5, else_branch5) = evaluate_condition(else_branch4[0])
assert cond5 == 'haiku'
assert len(if_branch5) == 1
validate_op('SOURCES', '+=', ['io/qstandardpaths_haiku.cpp'], if_branch5[0])
assert len(else_branch5) == 1
validate_op('SOURCES', '+=', ['io/qstandardpaths_unix.cpp'], else_branch5[0])
def test_realworld_comment_scope():
result = parse_file(_tests_path + '/data/comment_scope.pro')
assert len(result) == 2
(cond, if_branch, else_branch) = evaluate_condition(result[0])
assert cond == 'freebsd|openbsd'
assert len(if_branch) == 1
validate_op('QMAKE_LFLAGS_NOUNDEF', '=', [], if_branch[0])
assert 'included' in result[1]
assert result[1]['included'].get('value', '') == 'animation/animation.pri'
def test_realworld_contains_scope():
result = parse_file(_tests_path + '/data/contains_scope.pro')
assert len(result) == 2
def test_realworld_complex_assign():
result = parse_file(_tests_path + '/data/complex_assign.pro')
assert len(result) == 1
validate_op('qmake-clean.commands', '+=', '( cd qmake && $(MAKE) clean ":-(==)-:" \'(Foo)\' )'.split(),
result[0])
def test_realworld_complex_condition():
result = parse_file(_tests_path + '/data/complex_condition.pro')
assert len(result) == 1
(cond, if_branch, else_branch) = evaluate_condition(result[0])
assert cond == '!system("dbus-send --session --type=signal / ' \
'local.AutotestCheck.Hello >$$QMAKE_SYSTEM_NULL_DEVICE ' \
'2>&1")'
assert len(if_branch) == 1
validate_op('SOURCES', '=', ['dbus.cpp'], if_branch[0])
assert len(else_branch) == 0
def test_realworld_sql():
result = parse_file(_tests_path + '/data/sql.pro')
assert len(result) == 2
validate_op('TEMPLATE', '=', ['subdirs'], result[0])
validate_op('SUBDIRS', '=', ['kernel'], result[1])
def test_realworld_qtconfig():
result = parse_file(_tests_path + '/data/escaped_value.pro')
assert len(result) == 1
validate_op('MODULE_AUX_INCLUDES', '=', ['\\$\\$QT_MODULE_INCLUDE_BASE/QtANGLE'], result[0])
def test_realworld_lc():
result = parse_file(_tests_path + '/data/lc.pro')
assert len(result) == 3
def test_realworld_lc_with_comment_in_between():
result = parse_file(_tests_path + '/data/lc_with_comment.pro')
my_var = result[1]['value'][0]
assert my_var == 'foo'
my_var = result[2]['value'][0]
assert my_var == 'foo2'
my_var = result[3]['value'][0]
assert my_var == 'foo3'
my_var = result[4]['value'][0]
assert my_var == 'foo4'
my_var = result[5]['value'][0]
assert my_var == 'foo5'
sub_dirs = result[0]['value']
assert sub_dirs[0] == 'tga'
assert sub_dirs[1] == 'wbmp'
assert len(result) == 6
def test_condition_without_scope():
result = parse_file(_tests_path + '/data/condition_without_scope.pro')
assert len(result) == 1
def test_multi_condition_divided_by_lc():
result = parse_file(_tests_path + '/data/multi_condition_divided_by_lc.pro')
assert len(result) == 1
def test_nested_function_calls():
result = parse_file(_tests_path + '/data/nested_function_calls.pro')
assert len(result) == 1
def test_value_function():
result = parse_file(_tests_path + '/data/value_function.pro')
target = result[0]['value'][0]
assert target == 'Dummy'
value = result[1]['value']
assert value[0] == '$$TARGET'
def test_condition_operator_precedence():
result = parse_file(_tests_path + '/data/condition_operator_precedence.pro')
def validate_simplify(input_str: str, expected: str) -> None:
output = simplify_condition(map_condition(input_str))
assert output == expected
validate_simplify(result[0]["condition"], "a1 OR a2")
validate_simplify(result[1]["condition"], "b3 AND (b1 OR b2)")
validate_simplify(result[2]["condition"], "c4 OR (c1 AND c3) OR (c2 AND c3)")

View File

@ -0,0 +1,318 @@
#!/usr/bin/env python3
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from pro2cmake import Scope, SetOperation, merge_scopes, recursive_evaluate_scope
import pytest
import typing
ScopeList = typing.List[Scope]
def _map_to_operation(**kwargs):
result = {} # type: typing.Mapping[str, typing.List[SetOperation]]
for (key, value) in kwargs.items():
result[key] = [SetOperation([value])]
return result
def _new_scope(*, parent_scope=None, condition='', **kwargs) -> Scope:
return Scope(parent_scope=parent_scope,
qmake_file='file1', condition=condition, operations=_map_to_operation(**kwargs))
def _evaluate_scopes(scopes: ScopeList) -> ScopeList:
for s in scopes:
if not s.parent:
recursive_evaluate_scope(s)
return scopes
def _validate(input_scopes: ScopeList, output_scopes: ScopeList):
merged_scopes = merge_scopes(input_scopes)
assert merged_scopes == output_scopes
def test_evaluate_one_scope():
scope = _new_scope(condition='QT_FEATURE_foo', test1='bar')
input_scope = scope
recursive_evaluate_scope(scope)
assert scope == input_scope
def test_evaluate_child_scope():
scope = _new_scope(condition='QT_FEATURE_foo', test1='bar')
_new_scope(parent_scope=scope, condition='QT_FEATURE_bar', test2='bar')
input_scope = scope
recursive_evaluate_scope(scope)
assert scope.total_condition == 'QT_FEATURE_foo'
assert len(scope.children) == 1
assert scope.get_string('test1') == 'bar'
assert scope.get_string('test2', 'not found') == 'not found'
child = scope.children[0]
assert child.total_condition == 'QT_FEATURE_bar AND QT_FEATURE_foo'
assert child.get_string('test1', 'not found') == 'not found'
assert child.get_string('test2') == 'bar'
def test_evaluate_two_child_scopes():
scope = _new_scope(condition='QT_FEATURE_foo', test1='bar')
_new_scope(parent_scope=scope, condition='QT_FEATURE_bar', test2='bar')
_new_scope(parent_scope=scope, condition='QT_FEATURE_buz', test3='buz')
input_scope = scope
recursive_evaluate_scope(scope)
assert scope.total_condition == 'QT_FEATURE_foo'
assert len(scope.children) == 2
assert scope.get_string('test1') == 'bar'
assert scope.get_string('test2', 'not found') == 'not found'
assert scope.get_string('test3', 'not found') == 'not found'
child1 = scope.children[0]
assert child1.total_condition == 'QT_FEATURE_bar AND QT_FEATURE_foo'
assert child1.get_string('test1', 'not found') == 'not found'
assert child1.get_string('test2') == 'bar'
assert child1.get_string('test3', 'not found') == 'not found'
child2 = scope.children[1]
assert child2.total_condition == 'QT_FEATURE_buz AND QT_FEATURE_foo'
assert child2.get_string('test1', 'not found') == 'not found'
assert child2.get_string('test2') == ''
assert child2.get_string('test3', 'not found') == 'buz'
def test_evaluate_else_child_scopes():
scope = _new_scope(condition='QT_FEATURE_foo', test1='bar')
_new_scope(parent_scope=scope, condition='QT_FEATURE_bar', test2='bar')
_new_scope(parent_scope=scope, condition='else', test3='buz')
input_scope = scope
recursive_evaluate_scope(scope)
assert scope.total_condition == 'QT_FEATURE_foo'
assert len(scope.children) == 2
assert scope.get_string('test1') == 'bar'
assert scope.get_string('test2', 'not found') == 'not found'
assert scope.get_string('test3', 'not found') == 'not found'
child1 = scope.children[0]
assert child1.total_condition == 'QT_FEATURE_bar AND QT_FEATURE_foo'
assert child1.get_string('test1', 'not found') == 'not found'
assert child1.get_string('test2') == 'bar'
assert child1.get_string('test3', 'not found') == 'not found'
child2 = scope.children[1]
assert child2.total_condition == 'QT_FEATURE_foo AND NOT QT_FEATURE_bar'
assert child2.get_string('test1', 'not found') == 'not found'
assert child2.get_string('test2') == ''
assert child2.get_string('test3', 'not found') == 'buz'
def test_evaluate_invalid_else_child_scopes():
scope = _new_scope(condition='QT_FEATURE_foo', test1='bar')
_new_scope(parent_scope=scope, condition='else', test3='buz')
_new_scope(parent_scope=scope, condition='QT_FEATURE_bar', test2='bar')
input_scope = scope
with pytest.raises(AssertionError):
recursive_evaluate_scope(scope)
def test_merge_empty_scope_list():
_validate([], [])
def test_merge_one_scope():
scopes = [_new_scope(test='foo')]
recursive_evaluate_scope(scopes[0])
_validate(scopes, scopes)
def test_merge_one_on_scope():
scopes = [_new_scope(condition='ON', test='foo')]
recursive_evaluate_scope(scopes[0])
_validate(scopes, scopes)
def test_merge_one_off_scope():
scopes = [_new_scope(condition='OFF', test='foo')]
recursive_evaluate_scope(scopes[0])
_validate(scopes, [])
def test_merge_one_conditioned_scope():
scopes = [_new_scope(condition='QT_FEATURE_foo', test='foo')]
recursive_evaluate_scope(scopes[0])
_validate(scopes, scopes)
def test_merge_two_scopes_with_same_condition():
scopes = [_new_scope(condition='QT_FEATURE_bar', test='foo'),
_new_scope(condition='QT_FEATURE_bar', test2='bar')]
recursive_evaluate_scope(scopes[0])
recursive_evaluate_scope(scopes[1])
result = merge_scopes(scopes)
assert len(result) == 1
r0 = result[0]
assert r0.total_condition == 'QT_FEATURE_bar'
assert r0.get_string('test') == 'foo'
assert r0.get_string('test2') == 'bar'
def test_merge_three_scopes_two_with_same_condition():
scopes = [_new_scope(condition='QT_FEATURE_bar', test='foo'),
_new_scope(condition='QT_FEATURE_baz', test1='buz'),
_new_scope(condition='QT_FEATURE_bar', test2='bar')]
recursive_evaluate_scope(scopes[0])
recursive_evaluate_scope(scopes[1])
recursive_evaluate_scope(scopes[2])
result = merge_scopes(scopes)
assert len(result) == 2
r0 = result[0]
assert r0.total_condition == 'QT_FEATURE_bar'
assert r0.get_string('test') == 'foo'
assert r0.get_string('test2') == 'bar'
assert result[1] == scopes[1]
def test_merge_two_unrelated_on_off_scopes():
scopes = [_new_scope(condition='ON', test='foo'),
_new_scope(condition='OFF', test2='bar')]
recursive_evaluate_scope(scopes[0])
recursive_evaluate_scope(scopes[1])
_validate(scopes, [scopes[0]])
def test_merge_two_unrelated_on_off_scopes():
scopes = [_new_scope(condition='OFF', test='foo'),
_new_scope(condition='ON', test2='bar')]
recursive_evaluate_scope(scopes[0])
recursive_evaluate_scope(scopes[1])
_validate(scopes, [scopes[1]])
def test_merge_parent_child_scopes_with_different_conditions():
scope = _new_scope(condition='FOO', test1='parent')
scopes = [scope, _new_scope(parent_scope=scope, condition='bar', test2='child')]
recursive_evaluate_scope(scope)
_validate(scopes, scopes)
def test_merge_parent_child_scopes_with_same_conditions():
scope = _new_scope(condition='FOO AND bar', test1='parent')
scopes = [scope, _new_scope(parent_scope=scope, condition='FOO AND bar', test2='child')]
recursive_evaluate_scope(scope)
result = merge_scopes(scopes)
assert len(result) == 1
r0 = result[0]
assert r0.parent == None
assert r0.total_condition == 'FOO AND bar'
assert r0.get_string('test1') == 'parent'
assert r0.get_string('test2') == 'child'
def test_merge_parent_child_scopes_with_on_child_condition():
scope = _new_scope(condition='FOO AND bar', test1='parent')
scopes = [scope, _new_scope(parent_scope=scope, condition='ON', test2='child')]
recursive_evaluate_scope(scope)
result = merge_scopes(scopes)
assert len(result) == 1
r0 = result[0]
assert r0.parent == None
assert r0.total_condition == 'FOO AND bar'
assert r0.get_string('test1') == 'parent'
assert r0.get_string('test2') == 'child'
# Real world examples:
# qstandardpaths selection:
def test_qstandardpaths_scopes():
# top level:
scope1 = _new_scope(condition='ON', scope_id=1)
# win32 {
scope2 = _new_scope(parent_scope=scope1, condition='WIN32')
# !winrt {
# SOURCES += io/qstandardpaths_win.cpp
scope3 = _new_scope(parent_scope=scope2, condition='NOT WINRT',
SOURCES='qsp_win.cpp')
# } else {
# SOURCES += io/qstandardpaths_winrt.cpp
scope4 = _new_scope(parent_scope=scope2, condition='else',
SOURCES='qsp_winrt.cpp')
# }
# else: unix {
scope5 = _new_scope(parent_scope=scope1, condition='else')
scope6 = _new_scope(parent_scope=scope5, condition='UNIX')
# mac {
# OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm
scope7 = _new_scope(parent_scope=scope6, condition='MACOS', SOURCES='qsp_mac.mm')
# } else:android {
# SOURCES += io/qstandardpaths_android.cpp
scope8 = _new_scope(parent_scope=scope6, condition='else')
scope9 = _new_scope(parent_scope=scope8, condition='ANDROID AND NOT UNKNOWN_PLATFORM', SOURCES='qsp_android.cpp')
# } else:haiku {
# SOURCES += io/qstandardpaths_haiku.cpp
scope10 = _new_scope(parent_scope=scope8, condition='else')
scope11 = _new_scope(parent_scope=scope10, condition='HAIKU', SOURCES='qsp_haiku.cpp')
# } else {
# SOURCES +=io/qstandardpaths_unix.cpp
scope12 = _new_scope(parent_scope=scope10, condition='else', SOURCES='qsp_unix.cpp')
# }
# }
recursive_evaluate_scope(scope1)
assert scope1.total_condition == 'ON'
assert scope2.total_condition == 'WIN32'
assert scope3.total_condition == 'WIN32 AND NOT WINRT'
assert scope4.total_condition == 'WINRT'
assert scope5.total_condition == 'UNIX'
assert scope6.total_condition == 'UNIX'
assert scope7.total_condition == 'MACOS'
assert scope8.total_condition == 'UNIX AND NOT MACOS'
assert scope9.total_condition == 'ANDROID AND NOT UNKNOWN_PLATFORM'
assert scope10.total_condition == 'UNIX AND NOT MACOS AND (UNKNOWN_PLATFORM OR NOT ANDROID)'
assert scope11.total_condition == 'HAIKU AND (UNKNOWN_PLATFORM OR NOT ANDROID)'
assert scope12.total_condition == 'UNIX AND NOT HAIKU AND NOT MACOS AND (UNKNOWN_PLATFORM OR NOT ANDROID)'
def test_recursive_expansion():
scope = _new_scope(A='Foo',B='$$A/Bar')
assert scope.get_string('A') == 'Foo'
assert scope.get_string('B') == '$$A/Bar'
assert scope._expand_value('$$B/Source.cpp') == ['Foo/Bar/Source.cpp']
assert scope._expand_value('$$B') == ['Foo/Bar']

View File

@ -0,0 +1,79 @@
#!/usr/bin/env python3
# Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import urllib.request
# The original source for this data used to be
# 'https://git.fedorahosted.org/cgit/hwdata.git/plain/pnp.ids'
# which is discontinued. For now there seems to be a fork at:
url = 'https://github.com/vcrhonek/hwdata/raw/master/pnp.ids'
copyright = """
// Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
"""
notice = """/*
* This lookup table was generated from {}
*
* Do not change this file directly, instead edit the
* qtbase/util/edid/qedidvendortable.py script and regenerate this file.
*/""".format(url)
header = """
#ifndef QEDIDVENDORTABLE_P_H
#define QEDIDVENDORTABLE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
struct VendorTable {
const char id[4];
const char name[%d];
};
static const VendorTable q_edidVendorTable[] = {"""
footer = """};
QT_END_NAMESPACE
#endif // QEDIDVENDORTABLE_P_H"""
vendors = {}
max_vendor_length = 0
response = urllib.request.urlopen(url)
data = response.read().decode('utf-8')
for line in data.split('\n'):
l = line.split()
if line.startswith('#'):
continue
elif len(l) == 0:
continue
else:
pnp_id = l[0].upper()
vendors[pnp_id] = ' '.join(l[1:])
if len(vendors[pnp_id]) > max_vendor_length:
max_vendor_length = len(vendors[pnp_id])
print(copyright)
print(notice)
print(header % (max_vendor_length + 1))
for pnp_id in sorted(vendors.keys()):
print(' { "%s", "%s" },' % (pnp_id, vendors[pnp_id]))
print(footer)

147
util/glgen/README.txt Normal file
View File

@ -0,0 +1,147 @@
Overview
========
This is the glgen application used to generate OpenGL related classes from the
official Khronos OpenGL specification and typemap files.
To run this application download the gl.spec and gl.tm files from:
http://www.opengl.org/registry/api/gl.spec
http://www.opengl.org/registry/api/gl.tm
and place them into the application directory. These files are not stored in
the Qt Project's git repo or downloaded automatically to
a) avoid copyright issues
b) make sure the version of OpenGL used is controlled by a human
The glgen application parses these files and generates:
1) A set of public classes, one for each combination of OpenGL version and profile.
2) A set of backend helper classes that contain the actual function pointers
3) A factory class for the classes in 1)
4) A set of classes, one for each OpenGL extension which introduces new entry points
We will now describe each of these categories.
OpenGL Version and Profile Classes
==================================
The base class of this set is QAbstractOpenGLFunctions. From this we inherit one class
for each OpenGL version and if supported, profile.
The Core profile contains only the non-deprecated functionality. The Compatibility profile
also includes all functionality that was removed in OpenGL 3.1. Therefore, for OpenGL
3.2 onwards we have two classes for each version.
All of these classes are named with the following convention:
QOpenGLFunctions_<MAJOR>_<MINOR>[_PROFILE]
For example QOpenGLFunctions_2_1, QOpenGLFunctions_4_3_Core
The source and header files for these classes take the form
qopenglfunction_<MAJOR>_<MINOR>[_PROFILE].cpp
qopenglfunction_<MAJOR>_<MINOR>[_PROFILE].h
and should be moved to
$QTBASE/src/gui/opengl/
and forms part of the public QtGui library API.
Backend Helper Classes
======================
Every OpenGL function is categorised by which version it was introduced with and
whether it is part of the Core Profile and is deemed part of the core specification
or whther it is only part of the Compatibility profile and has been marked as
deprecated.
Glgen creates a backend helper class containing function pointers to match each
possible case. E.g. QOpenGLFunctions_1_5_CoreBackend contains functions introduced
in OpenGL 1.5 which are still core (not deprecated).
The public frontend classes described above contain pointers to the set of backend
objects necessary to implement the functions for their version and profile.
Creating new instances of these backend objects for each public version functions
object would be wasteful in terms of memory (repeated function pointers) and CPU
time (no need to keep re-solving the same functions).
We cannot share the backend objects globally as OpenGL entry point addresses are
specific to the OpenGL context. They cannot even be reliably shared between a
context group. This is not surprising if you consider the case of contexts in a share
group where the contexts have different versions or even profiles. We therefore share
the backend instances at the QOpenGLContext level using a simple reference counting
scheme.
When the frontend version functions objects are intialized they check to see if
the associated context already has suitable backend objects available. If so they use
them, otherwise they will create backend objects and associate them with the context.
The backend classes are in
qopenglversionfunctions.h
qopenglversionfunctions.cpp
and should also be moved to
$QTBASE/src/gui/opengl/
OpenGL Version and Profile Factory
==================================
Instances of the OpenGL version and profile classes described above can be obtained
from QOpenGLContext by means of the versionFunctions() member. The OpenGLContext
retains ownership of the QOpenGLFunctions_* object. If a suitable object does not
already exist it is created by the factory class generated by glgen.
It is possible to request version functions objects for any version/profile
combination from a context. However not all requests can be serviced. For example
consider the case of an OpenGL 3.3 Core profile context. In this case:
* Requesting a 3.3 core profile functions object would succeed.
* Requesting a 3.3 compatibility profile functions object would fail. We would fail
to resolve the deprecated functions.
* Requesting a 4.3 core profile functions object would fail. We would fail to resolve
the new core functions introduced in versions 4.0-4.3.
* Requesting a 3.1 functions object would succeed. There is nothing in 3.1 that is not
also in 3.3 core.
If a request is not able to be serviced the factory, and hence QOpenGLContext::versionFunctions()
will return a null pointer that can be checked for.
The source and header file for this class should be moved to
$QTBASE/src/gui/opengl/
and forms part of the QtGui library.
If a user instantiates a version functions object directly (i.e. not via QOpenGLContext)
then it bypasses the above checks. However, the same checks are applied in the
initializeOpenGLFunctions() method and the result can once again be checked.
This approach allows maximum flexibility but ensure's safety in that once the user
posesses a functions object that has been successfully initialized they can rely upon its
member functions being successfully resolved.
OpenGL Extension Classes
========================
In addition, glgen also creates one class for each OpenGL extension that introduces
new entry points. These classes are named with the convention
QOpenGLExtension_<name-of-extension>
The usage pattern for OpenGL extensions is to just use a small
number of extensions out of the large number of those available.
Prior to Qt 6, these classes were provided as the openglextensions
module. Because of the new graphics architecture in Qt 6, that module
has been removed.

1080
util/glgen/codegenerator.cpp Normal file

File diff suppressed because it is too large Load Diff

111
util/glgen/codegenerator.h Normal file
View File

@ -0,0 +1,111 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CODEGENERATOR_H
#define CODEGENERATOR_H
#include "specparser.h"
class QTextStream;
class CodeGenerator
{
public:
explicit CodeGenerator();
void setParser(SpecParser *parser) {m_parser = parser;}
void generateCoreClasses(const QString &baseFileName) const;
void generateExtensionClasses(const QString &baseFileName) const;
private:
// Generic support
enum ClassVisibility {
Public,
Private
};
enum ClassComponent {
Declaration = 0,
Definition
};
bool isLegacyVersion(Version v) const;
bool versionHasProfiles(Version v) const;
FunctionCollection functionCollection( const VersionProfile& classVersionProfile ) const;
void writePreamble(const QString &baseFileName, QTextStream &stream, const QString replacement = QString()) const;
void writePostamble(const QString &baseFileName, QTextStream &stream) const;
QString passByType(const Argument &arg) const;
QString safeArgumentName(const QString& arg) const;
// Core functionality support
QString coreClassFileName(const VersionProfile &versionProfile,
const QString& fileExtension) const;
void writeCoreHelperClasses(const QString &fileName, ClassComponent component) const;
void writeCoreClasses(const QString &baseFileName) const;
void writeCoreFactoryHeader(const QString &fileName) const;
void writeCoreFactoryImplementation(const QString &fileName) const;
QString generateClassName(const VersionProfile &classVersion, ClassVisibility visibility = Public) const;
void writeBackendClassDeclaration(QTextStream &stream,
const VersionProfile &versionProfile,
const QString &baseClass) const;
void writeBackendClassImplementation(QTextStream &stream,
const VersionProfile &versionProfile,
const QString &baseClass) const;
void writePublicClassDeclaration(const QString &baseFileName,
const VersionProfile &versionProfile,
const QString &baseClass) const;
void writePublicClassImplementation(const QString &baseFileName,
const VersionProfile &versionProfile,
const QString& baseClass) const;
void writeClassFunctionDeclarations(QTextStream &stream,
const FunctionCollection &functionSets,
ClassVisibility visibility) const;
void writeFunctionDeclaration(QTextStream &stream, const Function &f, ClassVisibility visibility) const;
void writeClassInlineFunctions(QTextStream &stream,
const QString &className,
const FunctionCollection &functionSet) const;
void writeInlineFunction(QTextStream &stream, const QString &className,
const QString &backendVar, const Function &f) const;
void writeEntryPointResolutionCode(QTextStream &stream,
const FunctionCollection &functionSet) const;
void writeEntryPointResolutionStatement(QTextStream &stream, const Function &f,
const QString &prefix = QString(), bool useGetProcAddress = false) const;
QList<VersionProfile> backendsForFunctionCollection(const FunctionCollection &functionSet) const;
QString backendClassName(const VersionProfile &v) const;
QString backendVariableName(const VersionProfile &v) const;
void writeBackendVariableDeclarations(QTextStream &stream, const QList<VersionProfile> &backends) const;
// Extension class support
void writeExtensionHeader(const QString &fileName) const;
void writeExtensionImplementation(const QString &fileName) const;
void writeExtensionClassDeclaration(QTextStream &stream,
const QString &extension,
ClassVisibility visibility = Public) const;
void writeExtensionClassImplementation(QTextStream &stream, const QString &extension) const;
QString generateExtensionClassName(const QString &extension, ClassVisibility visibility = Public) const;
void writeExtensionInlineFunction(QTextStream &stream, const QString &className, const Function &f) const;
SpecParser *m_parser;
mutable QMap<QString, int> m_extensionIds;
};
#endif // CODEGENERATOR_H

38
util/glgen/glgen.pro Normal file
View File

@ -0,0 +1,38 @@
QT -= gui
QT += core5compat
CONFIG += cmdline
# Uncomment following to enable debug output
#DEFINES += SPECPARSER_DEBUG
TEMPLATE = app
TARGET = glgen
SOURCES += main.cpp \
xmlspecparser.cpp \
legacyspecparser.cpp \
codegenerator.cpp
HEADERS += \
specparser.h \
xmlspecparser.h \
legacyspecparser.h \
codegenerator.h
OTHER_FILES += \
qopenglversionfunctions.h.header \
qopenglversionfunctions.h.footer \
qopenglversionfunctions.cpp.header \
qopenglversionfunctions.cpp.footer \
qopenglversionfunctions__VERSION__.cpp.footer \
qopenglversionfunctions__VERSION__.cpp.header \
qopenglversionfunctions__VERSION__.h.footer \
qopenglversionfunctions__VERSION__.h.header \
qopenglversionfunctionsfactory_p.h.header \
qopenglextensions.cpp.header \
qopenglextensions.cpp.footer \
qopenglextensions.h.header \
qopenglextensions.h.footer \
qopenglversionfunctionsfactory.cpp.header \
qopenglversionfunctionsfactory.cpp.footer \
README.txt

View File

@ -0,0 +1,275 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "legacyspecparser.h"
#include <QDebug>
#include <QFile>
#include <QRegExp>
#include <QStringList>
#include <QTextStream>
#ifdef SPECPARSER_DEBUG
#define qLegacySpecParserDebug qDebug
#else
#define qLegacySpecParserDebug QT_NO_QDEBUG_MACRO
#endif
bool LegacySpecParser::parse()
{
// Get the mapping form generic types to specific types suitable for use in C-headers
if (!parseTypeMap())
return false;
// Open up a stream on the actual OpenGL function spec file
QFile file(specFileName());
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Failed to open spec file:" << specFileName() << "Aborting";
return false;
}
QTextStream stream(&file);
// Extract the info that we need
parseFunctions(stream);
return true;
}
bool LegacySpecParser::parseTypeMap()
{
QFile file(typeMapFileName());
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Failed to open type file:" << typeMapFileName() << "Aborting";
return false;
}
QTextStream stream(&file);
static QRegExp typeMapRegExp("([^,]+)\\W+([^,]+)");
while (!stream.atEnd()) {
QString line = stream.readLine();
if (line.startsWith(QLatin1Char('#')))
continue;
if (typeMapRegExp.indexIn(line) != -1) {
QString key = typeMapRegExp.cap(1).simplified();
QString value = typeMapRegExp.cap(2).simplified();
// Special case for void
if (value == QLatin1String("*"))
value = QStringLiteral("void");
m_typeMap.insert(key, value);
qLegacySpecParserDebug() << "Found type mapping from" << key << "=>" << value;
}
}
return true;
}
void LegacySpecParser::parseEnums()
{
}
void LegacySpecParser::parseFunctions(QTextStream &stream)
{
static QRegExp functionRegExp("^(\\w+)\\(.*\\)");
static QRegExp returnRegExp("^\\treturn\\s+(\\S+)");
static QRegExp argumentRegExp("param\\s+(\\S+)\\s+(\\S+) (\\S+) (\\S+)");
static QRegExp versionRegExp("^\\tversion\\s+(\\S+)");
static QRegExp deprecatedRegExp("^\\tdeprecated\\s+(\\S+)");
static QRegExp categoryRegExp("^\\tcategory\\s+(\\S+)");
static QRegExp categoryVersionRegExp("VERSION_(\\d)_(\\d)");
static QRegExp extToCoreVersionRegExp("passthru:\\s/\\*\\sOpenGL\\s(\\d)\\.(\\d)\\s.*\\sextensions:");
static QRegExp extToCoreRegExp("passthru:\\s/\\*\\s(ARB_\\S*)\\s.*\\*/");
Function currentFunction;
VersionProfile currentVersionProfile;
QString currentCategory;
bool haveVersionInfo = false;
bool acceptCurrentFunctionInCore = false;
bool acceptCurrentFunctionInExtension = false;
QHash<QString, Version> extensionsNowInCore;
Version extToCoreCurrentVersion;
int functionCount = 0;
QSet<Version> versions;
while (!stream.atEnd()) {
QString line = stream.readLine();
if (line.startsWith("#"))
continue;
if (functionRegExp.indexIn(line) != -1) {
if (!currentFunction.name.isEmpty()) {
// NB - Special handling!
// Versions 4.2 and 4.3 (and probably newer) add functionality by
// subsuming extensions such as ARB_texture_storage. However, some extensions
// also include functions to interact with the EXT_direct_state_access
// extension. These functions should be added to the DSA extension rather
// than the core functionality. The core will already contain non-DSA
// versions of these functions.
if (acceptCurrentFunctionInCore && currentFunction.name.endsWith(QLatin1String("EXT"))) {
acceptCurrentFunctionInCore = false;
acceptCurrentFunctionInExtension = true;
currentCategory = QStringLiteral("EXT_direct_state_access");
}
// Finish off previous function (if any) by inserting it into the core
// functionality or extension functionality (or both)
if (acceptCurrentFunctionInCore) {
m_functions.insert(currentVersionProfile, currentFunction);
versions.insert(currentVersionProfile.version);
}
if (acceptCurrentFunctionInExtension) {
FunctionProfile fp;
fp.profile = currentVersionProfile.profile;
fp.function = currentFunction;
m_extensionFunctions.insert(currentCategory, fp);
}
}
// Start a new function
++functionCount;
haveVersionInfo = false;
acceptCurrentFunctionInCore = true;
acceptCurrentFunctionInExtension = false;
currentCategory = QString();
currentFunction = Function();
// We assume a core function unless we find a deprecated flag (see below)
currentVersionProfile = VersionProfile();
currentVersionProfile.profile = VersionProfile::CoreProfile;
// Extract the function name
QString functionName = functionRegExp.cap(1);
currentFunction.name = functionName;
qLegacySpecParserDebug() << "Found function:" << functionName;
} else if (argumentRegExp.indexIn(line) != -1) {
// Extract info about this function argument
Argument arg;
arg.name = argumentRegExp.cap(1);
QString type = argumentRegExp.cap(2); // Lookup in type map
arg.type = m_typeMap.value(type);
QString direction = argumentRegExp.cap(3);
if (direction == QLatin1String("in")) {
arg.direction = Argument::In;
} else if (direction == QLatin1String("out")) {
arg.direction = Argument::Out;
} else {
qWarning() << "Invalid argument direction found:" << direction;
acceptCurrentFunctionInCore = false;
}
QString mode = argumentRegExp.cap(4);
if (mode == QLatin1String("value")) {
arg.mode = Argument::Value;
} else if (mode == QLatin1String("array")) {
arg.mode = Argument::Array;
} else if (mode == QLatin1String("reference")) {
arg.mode = Argument::Reference;
} else {
qWarning() << "Invalid argument mode found:" << mode;
acceptCurrentFunctionInCore = false;
}
qLegacySpecParserDebug() << " argument:" << arg.type << arg.name;
currentFunction.arguments.append(arg);
} else if (returnRegExp.indexIn(line) != -1) {
// Lookup the return type from the typemap
QString returnTypeKey = returnRegExp.cap(1).simplified();
if (!m_typeMap.contains(returnTypeKey)) {
qWarning() << "Unknown return type found:" << returnTypeKey;
acceptCurrentFunctionInCore = false;
}
QString returnType = m_typeMap.value(returnTypeKey);
qLegacySpecParserDebug() << " return type:" << returnType;
currentFunction.returnType = returnType;
} else if (versionRegExp.indexIn(line) != -1 && !haveVersionInfo) { // Only use version line if no other source
// Extract the OpenGL version in which this function was introduced
QString version = versionRegExp.cap(1);
qLegacySpecParserDebug() << " version:" << version;
QStringList parts = version.split(QLatin1Char('.'));
if (parts.size() != 2) {
qWarning() << "Found invalid version number";
continue;
}
int majorVersion = parts.first().toInt();
int minorVersion = parts.last().toInt();
Version v;
v.major = majorVersion;
v.minor = minorVersion;
currentVersionProfile.version = v;
} else if (deprecatedRegExp.indexIn(line) != -1) {
// Extract the OpenGL version in which this function was deprecated.
// If it is OpenGL 3.1 then it must be a compatibility profile function
QString deprecatedVersion = deprecatedRegExp.cap(1).simplified();
if (deprecatedVersion == QLatin1String("3.1") && !inDeprecationException(currentFunction.name))
currentVersionProfile.profile = VersionProfile::CompatibilityProfile;
} else if (categoryRegExp.indexIn(line) != -1) {
// Extract the category for this function
QString category = categoryRegExp.cap(1).simplified();
qLegacySpecParserDebug() << " category:" << category;
if (categoryVersionRegExp.indexIn(category) != -1) {
// Use the version info in the category in preference to the version
// entry as this is more applicable and consistent
int majorVersion = categoryVersionRegExp.cap(1).toInt();
int minorVersion = categoryVersionRegExp.cap(2).toInt();
Version v;
v.major = majorVersion;
v.minor = minorVersion;
currentVersionProfile.version = v;
haveVersionInfo = true;
} else {
// Make a note of the extension name and tag this function as being part of an extension
qLegacySpecParserDebug() << "Found category =" << category;
currentCategory = category;
acceptCurrentFunctionInExtension = true;
// See if this category (extension) is in our set of extensions that
// have now been folded into the core feature set
if (extensionsNowInCore.contains(category)) {
currentVersionProfile.version = extensionsNowInCore.value(category);
haveVersionInfo = true;
} else {
acceptCurrentFunctionInCore = false;
}
}
} else if (extToCoreVersionRegExp.indexIn(line) != -1) {
qLegacySpecParserDebug() << line;
int majorVersion = extToCoreVersionRegExp.cap(1).toInt();
int minorVersion = extToCoreVersionRegExp.cap(2).toInt();
extToCoreCurrentVersion.major = majorVersion;
extToCoreCurrentVersion.minor = minorVersion;
} else if (extToCoreRegExp.indexIn(line) != -1) {
QString extension = extToCoreRegExp.cap(1);
extensionsNowInCore.insert(extension, extToCoreCurrentVersion);
}
}
m_versions = versions.values();
std::sort(m_versions.begin(), m_versions.end());
}
bool LegacySpecParser::inDeprecationException(const QString &functionName) const
{
return functionName == QLatin1String("TexImage3D");
}

View File

@ -0,0 +1,40 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef LEGACYSPECPARSER_H
#define LEGACYSPECPARSER_H
#include "specparser.h"
#include <QStringList>
#include <QVariant>
class QTextStream;
class LegacySpecParser : public SpecParser
{
public:
virtual QList<Version> versions() const {return m_versions;}
virtual bool parse();
protected:
const QMultiHash<VersionProfile, Function> &versionFunctions() const { return m_functions; }
const QMultiMap<QString, FunctionProfile> &extensionFunctions() const { return m_extensionFunctions; }
private:
QMap<QString, QString> m_typeMap;
QMultiHash<VersionProfile, Function> m_functions;
QList<Version> m_versions;
// Extension support
QMultiMap<QString, FunctionProfile> m_extensionFunctions;
bool parseTypeMap();
void parseEnums();
void parseFunctions(QTextStream &stream);
bool inDeprecationException(const QString &functionName) const;
};
#endif // LEGACYSPECPARSER_H

40
util/glgen/main.cpp Normal file
View File

@ -0,0 +1,40 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "codegenerator.h"
#include "legacyspecparser.h"
#include "xmlspecparser.h"
#include <QCommandLineParser>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QCommandLineParser cmdParser;
// flag whether to use legacy or not
QCommandLineOption legacyOption(QStringList() << "l" << "legacy", "Use legacy parser.");
cmdParser.addOption(legacyOption);
cmdParser.process(app);
SpecParser *parser;
if (cmdParser.isSet(legacyOption)) {
parser = new LegacySpecParser();
parser->setTypeMapFileName(QStringLiteral("gl.tm"));
parser->setSpecFileName(QStringLiteral("gl.spec"));
} else {
parser = new XmlSpecParser();
parser->setSpecFileName(QStringLiteral("gl.xml"));
}
parser->parse();
CodeGenerator generator;
generator.setParser(parser);
generator.generateCoreClasses(QStringLiteral("qopenglversionfunctions"));
generator.generateExtensionClasses(QStringLiteral("qopenglextensions"));
delete parser;
return 0;
}

View File

@ -0,0 +1,792 @@
#else
QOpenGLExtension_OES_EGL_image::QOpenGLExtension_OES_EGL_image()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_OES_EGL_imagePrivate))
{
}
bool QOpenGLExtension_OES_EGL_image::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_OES_EGL_image);
d->EGLImageTargetTexture2DOES = (void (QOPENGLF_APIENTRYP)(GLenum target, GLeglImageOES image))context->getProcAddress("glEGLImageTargetTexture2DOES");
d->EGLImageTargetRenderbufferStorageOES = (void (QOPENGLF_APIENTRYP)(GLenum target, GLeglImageOES image))context->getProcAddress("glEGLImageTargetRenderbufferStorageOES");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_OES_get_program_binary::QOpenGLExtension_OES_get_program_binary()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_OES_get_program_binaryPrivate))
{
}
bool QOpenGLExtension_OES_get_program_binary::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_OES_get_program_binary);
d->GetProgramBinaryOES = (void (QOPENGLF_APIENTRYP)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary))context->getProcAddress("glGetProgramBinaryOES");
d->ProgramBinaryOES = (void (QOPENGLF_APIENTRYP)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length))context->getProcAddress("glProgramBinaryOES");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_OES_mapbuffer::QOpenGLExtension_OES_mapbuffer()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_OES_mapbufferPrivate))
{
}
bool QOpenGLExtension_OES_mapbuffer::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_OES_mapbuffer);
d->MapBufferOES = (void* (QOPENGLF_APIENTRYP)(GLenum target, GLenum access))context->getProcAddress("glMapBufferOES");
d->UnmapBufferOES = (GLboolean (QOPENGLF_APIENTRYP)(GLenum target))context->getProcAddress("glUnmapBufferOES");
d->GetBufferPointervOES = (void (QOPENGLF_APIENTRYP)(GLenum target, GLenum pname, GLvoid** params))context->getProcAddress("glGetBufferPointervOES");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_OES_texture_3D::QOpenGLExtension_OES_texture_3D()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_OES_texture_3DPrivate))
{
}
bool QOpenGLExtension_OES_texture_3D::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_OES_texture_3D);
d->TexImage3DOES = (void (QOPENGLF_APIENTRYP)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels))context->getProcAddress("glTexImage3DOES");
d->TexSubImage3DOES = (void (QOPENGLF_APIENTRYP)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels))context->getProcAddress("glTexSubImage3DOES");
d->CopyTexSubImage3DOES = (void (QOPENGLF_APIENTRYP)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height))context->getProcAddress("glCopyTexSubImage3DOES");
d->CompressedTexImage3DOES = (void (QOPENGLF_APIENTRYP)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data))context->getProcAddress("glCompressedTexImage3DOES");
d->CompressedTexSubImage3DOES = (void (QOPENGLF_APIENTRYP)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data))context->getProcAddress("glCompressedTexSubImage3DOES");
d->FramebufferTexture3DOES = (void (QOPENGLF_APIENTRYP)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset))context->getProcAddress("glFramebufferTexture3DOES");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_OES_vertex_array_object::QOpenGLExtension_OES_vertex_array_object()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_OES_vertex_array_objectPrivate))
{
}
bool QOpenGLExtension_OES_vertex_array_object::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_OES_vertex_array_object);
d->BindVertexArrayOES = (void (QOPENGLF_APIENTRYP)(GLuint array))context->getProcAddress("glBindVertexArrayOES");
d->DeleteVertexArraysOES = (void (QOPENGLF_APIENTRYP)(GLsizei n, const GLuint *arrays))context->getProcAddress("glDeleteVertexArraysOES");
d->GenVertexArraysOES = (void (QOPENGLF_APIENTRYP)(GLsizei n, GLuint *arrays))context->getProcAddress("glGenVertexArraysOES");
d->IsVertexArrayOES = (GLboolean (QOPENGLF_APIENTRYP)(GLuint array))context->getProcAddress("glIsVertexArrayOES");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_AMD_performance_monitor::QOpenGLExtension_AMD_performance_monitor()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_AMD_performance_monitorPrivate))
{
}
bool QOpenGLExtension_AMD_performance_monitor::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_AMD_performance_monitor);
d->GetPerfMonitorGroupsAMD = (void (QOPENGLF_APIENTRYP)(GLint *numGroups, GLsizei groupsSize, GLuint *groups))context->getProcAddress("glGetPerfMonitorGroupsAMD");
d->GetPerfMonitorCountersAMD = (void (QOPENGLF_APIENTRYP)(GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters))context->getProcAddress("glGetPerfMonitorCountersAMD");
d->GetPerfMonitorGroupStringAMD = (void (QOPENGLF_APIENTRYP)(GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString))context->getProcAddress("glGetPerfMonitorGroupStringAMD");
d->GetPerfMonitorCounterStringAMD = (void (QOPENGLF_APIENTRYP)(GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString))context->getProcAddress("glGetPerfMonitorCounterStringAMD");
d->GetPerfMonitorCounterInfoAMD = (void (QOPENGLF_APIENTRYP)(GLuint group, GLuint counter, GLenum pname, GLvoid *data))context->getProcAddress("glGetPerfMonitorCounterInfoAMD");
d->GenPerfMonitorsAMD = (void (QOPENGLF_APIENTRYP)(GLsizei n, GLuint *monitors))context->getProcAddress("glGenPerfMonitorsAMD");
d->DeletePerfMonitorsAMD = (void (QOPENGLF_APIENTRYP)(GLsizei n, GLuint *monitors))context->getProcAddress("glDeletePerfMonitorsAMD");
d->SelectPerfMonitorCountersAMD = (void (QOPENGLF_APIENTRYP)(GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList))context->getProcAddress("glSelectPerfMonitorCountersAMD");
d->BeginPerfMonitorAMD = (void (QOPENGLF_APIENTRYP)(GLuint monitor))context->getProcAddress("glBeginPerfMonitorAMD");
d->EndPerfMonitorAMD = (void (QOPENGLF_APIENTRYP )(GLuint monitor))context->getProcAddress("glEndPerfMonitorAMD");
d->GetPerfMonitorCounterDataAMD = (void (QOPENGLF_APIENTRYP)(GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten))context->getProcAddress("glGetPerfMonitorCounterDataAMD");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_ANGLE_framebuffer_blit::QOpenGLExtension_ANGLE_framebuffer_blit()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_ANGLE_framebuffer_blitPrivate))
{
}
bool QOpenGLExtension_ANGLE_framebuffer_blit::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_ANGLE_framebuffer_blit);
d->BlitFramebufferANGLE = (void (QOPENGLF_APIENTRYP)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter))context->getProcAddress("glBlitFramebufferANGLE");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_ANGLE_framebuffer_multisample::QOpenGLExtension_ANGLE_framebuffer_multisample()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_ANGLE_framebuffer_multisamplePrivate))
{
}
bool QOpenGLExtension_ANGLE_framebuffer_multisample::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_ANGLE_framebuffer_multisample);
d->RenderbufferStorageMultisampleANGLE = (void (QOPENGLF_APIENTRYP)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height))context->getProcAddress("glRenderbufferStorageMultisampleANGLE");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_ANGLE_instanced_arrays::QOpenGLExtension_ANGLE_instanced_arrays()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_ANGLE_instanced_arraysPrivate))
{
}
bool QOpenGLExtension_ANGLE_instanced_arrays::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_ANGLE_instanced_arrays);
d->DrawArraysInstancedANGLE = (void (QOPENGLF_APIENTRYP)(GLenum mode, GLint first, GLsizei count, GLsizei primcount))context->getProcAddress("glDrawArraysInstancedANGLE");
d->DrawElementsInstancedANGLE = (void (QOPENGLF_APIENTRYP)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount))context->getProcAddress("glDrawElementsInstancedANGLE");
d->VertexAttribDivisorANGLE = (void (QOPENGLF_APIENTRYP)(GLuint index, GLuint divisor))context->getProcAddress("glVertexAttribDivisorANGLE");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_ANGLE_translated_shader_source::QOpenGLExtension_ANGLE_translated_shader_source()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_ANGLE_translated_shader_sourcePrivate))
{
}
bool QOpenGLExtension_ANGLE_translated_shader_source::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_ANGLE_translated_shader_source);
d->GetTranslatedShaderSourceANGLE = (void (QOPENGLF_APIENTRYP)(GLuint shader, GLsizei bufsize, GLsizei *length, GLchar *source))context->getProcAddress("glGetTranslatedShaderSourceANGLE");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_APPLE_framebuffer_multisample::QOpenGLExtension_APPLE_framebuffer_multisample()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_APPLE_framebuffer_multisamplePrivate))
{
}
bool QOpenGLExtension_APPLE_framebuffer_multisample::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_APPLE_framebuffer_multisample);
d->RenderbufferStorageMultisampleAPPLE = (void (QOPENGLF_APIENTRYP)(GLenum, GLsizei, GLenum, GLsizei, GLsizei))context->getProcAddress("glRenderbufferStorageMultisampleAPPLE");
d->ResolveMultisampleFramebufferAPPLE = (void (QOPENGLF_APIENTRYP)(void))context->getProcAddress("glResolveMultisampleFramebufferAPPLE");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_EXT_debug_label::QOpenGLExtension_EXT_debug_label()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_EXT_debug_labelPrivate))
{
}
bool QOpenGLExtension_EXT_debug_label::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_EXT_debug_label);
d->LabelObjectEXT = (void (QOPENGLF_APIENTRYP)(GLenum type, GLuint object, GLsizei length, const GLchar *label))context->getProcAddress("glLabelObjectEXT");
d->GetObjectLabelEXT = (void (QOPENGLF_APIENTRYP)(GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label))context->getProcAddress("glGetObjectLabelEXT");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_EXT_debug_marker::QOpenGLExtension_EXT_debug_marker()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_EXT_debug_markerPrivate))
{
}
bool QOpenGLExtension_EXT_debug_marker::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_EXT_debug_marker);
d->InsertEventMarkerEXT = (void (QOPENGLF_APIENTRYP)(GLsizei length, const GLchar *marker))context->getProcAddress("glInsertEventMarkerEXT");
d->PushGroupMarkerEXT = (void (QOPENGLF_APIENTRYP)(GLsizei length, const GLchar *marker))context->getProcAddress("glPushGroupMarkerEXT");
d->PopGroupMarkerEXT = (void (QOPENGLF_APIENTRYP)(void))context->getProcAddress("glPopGroupMarkerEXT");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_EXT_discard_framebuffer::QOpenGLExtension_EXT_discard_framebuffer()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_EXT_discard_framebufferPrivate))
{
}
bool QOpenGLExtension_EXT_discard_framebuffer::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_EXT_discard_framebuffer);
d->DiscardFramebufferEXT = (void (QOPENGLF_APIENTRYP)(GLenum target, GLsizei numAttachments, const GLenum *attachments))context->getProcAddress("glDiscardFramebufferEXT");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_EXT_multisampled_render_to_texture::QOpenGLExtension_EXT_multisampled_render_to_texture()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_EXT_multisampled_render_to_texturePrivate))
{
}
bool QOpenGLExtension_EXT_multisampled_render_to_texture::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_EXT_multisampled_render_to_texture);
d->RenderbufferStorageMultisampleEXT = (void (QOPENGLF_APIENTRYP)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height))context->getProcAddress("glRenderbufferStorageMultisampleEXT");
d->FramebufferTexture2DMultisampleEXT = (void (QOPENGLF_APIENTRYP)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples))context->getProcAddress("glFramebufferTexture2DMultisampleEXT");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_EXT_multi_draw_arrays::QOpenGLExtension_EXT_multi_draw_arrays()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_EXT_multi_draw_arraysPrivate))
{
}
bool QOpenGLExtension_EXT_multi_draw_arrays::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_EXT_multi_draw_arrays);
d->MultiDrawArraysEXT = (void (QOPENGLF_APIENTRYP)(GLenum mode, GLint *first, GLsizei *count, GLsizei primcount))context->getProcAddress("glMultiDrawArraysEXT");
d->MultiDrawElementsEXT = (void (QOPENGLF_APIENTRYP)(GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount))context->getProcAddress("glMultiDrawElementsEXT");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_EXT_occlusion_query_boolean::QOpenGLExtension_EXT_occlusion_query_boolean()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_EXT_occlusion_query_booleanPrivate))
{
}
bool QOpenGLExtension_EXT_occlusion_query_boolean::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_EXT_occlusion_query_boolean);
d->GenQueriesEXT = (void (QOPENGLF_APIENTRYP)(GLsizei n, GLuint *ids))context->getProcAddress("glGenQueriesEXT");
d->DeleteQueriesEXT = (void (QOPENGLF_APIENTRYP)(GLsizei n, const GLuint *ids))context->getProcAddress("glDeleteQueriesEXT");
d->IsQueryEXT = (GLboolean (QOPENGLF_APIENTRYP)(GLuint id))context->getProcAddress("glIsQueryEXT");
d->BeginQueryEXT = (void (QOPENGLF_APIENTRYP)(GLenum target, GLuint id))context->getProcAddress("glBeginQueryEXT");
d->EndQueryEXT = (void (QOPENGLF_APIENTRYP)(GLenum target))context->getProcAddress("glEndQueryEXT");
d->GetQueryivEXT = (void (QOPENGLF_APIENTRYP)(GLenum target, GLenum pname, GLint *params))context->getProcAddress("glGetQueryivEXT");
d->GetQueryObjectuivEXT = (void (QOPENGLF_APIENTRYP)(GLuint id, GLenum pname, GLuint *params))context->getProcAddress("glGetQueryObjectuivEXT");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_EXT_robustness::QOpenGLExtension_EXT_robustness()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_EXT_robustnessPrivate))
{
}
bool QOpenGLExtension_EXT_robustness::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_EXT_robustness);
d->GetGraphicsResetStatusEXT = (GLenum (QOPENGLF_APIENTRYP)(void))context->getProcAddress("glGetGraphicsResetStatusEXT");
d->ReadnPixelsEXT = (void (QOPENGLF_APIENTRYP)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data))context->getProcAddress("glReadnPixelsEXT");
d->GetnUniformfvEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei bufSize, float *params))context->getProcAddress("glGetnUniformfvEXT");
d->GetnUniformivEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei bufSize, GLint *params))context->getProcAddress("glGetnUniformivEXT");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_EXT_separate_shader_objects::QOpenGLExtension_EXT_separate_shader_objects()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_EXT_separate_shader_objectsPrivate))
{
}
bool QOpenGLExtension_EXT_separate_shader_objects::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_EXT_separate_shader_objects);
d->UseProgramStagesEXT = (void (QOPENGLF_APIENTRYP)(GLuint pipeline, GLbitfield stages, GLuint program))context->getProcAddress("glUseProgramStagesEXT");
d->ActiveShaderProgramEXT = (void (QOPENGLF_APIENTRYP)(GLuint pipeline, GLuint program))context->getProcAddress("glActiveShaderProgramEXT");
d->CreateShaderProgramvEXT = (GLuint (QOPENGLF_APIENTRYP)(GLenum type, GLsizei count, const GLchar **strings))context->getProcAddress("glCreateShaderProgramvEXT");
d->BindProgramPipelineEXT = (void (QOPENGLF_APIENTRYP)(GLuint pipeline))context->getProcAddress("glBindProgramPipelineEXT");
d->DeleteProgramPipelinesEXT = (void (QOPENGLF_APIENTRYP)(GLsizei n, const GLuint *pipelines))context->getProcAddress("glDeleteProgramPipelinesEXT");
d->GenProgramPipelinesEXT = (void (QOPENGLF_APIENTRYP)(GLsizei n, GLuint *pipelines))context->getProcAddress("glGenProgramPipelinesEXT");
d->IsProgramPipelineEXT = (GLboolean (QOPENGLF_APIENTRYP)(GLuint pipeline))context->getProcAddress("glIsProgramPipelineEXT");
d->ProgramParameteriEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLenum pname, GLint value))context->getProcAddress("glProgramParameteriEXT");
d->GetProgramPipelineivEXT = (void (QOPENGLF_APIENTRYP)(GLuint pipeline, GLenum pname, GLint *params))context->getProcAddress("glGetProgramPipelineivEXT");
d->ProgramUniform1iEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLint x))context->getProcAddress("glProgramUniform1iEXT");
d->ProgramUniform2iEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLint x, GLint y))context->getProcAddress("glProgramUniform2iEXT");
d->ProgramUniform3iEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLint x, GLint y, GLint z))context->getProcAddress("glProgramUniform3iEXT");
d->ProgramUniform4iEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLint x, GLint y, GLint z, GLint w))context->getProcAddress("glProgramUniform4iEXT");
d->ProgramUniform1fEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLfloat x))context->getProcAddress("glProgramUniform1fEXT");
d->ProgramUniform2fEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLfloat x, GLfloat y))context->getProcAddress("glProgramUniform2fEXT");
d->ProgramUniform3fEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLfloat x, GLfloat y, GLfloat z))context->getProcAddress("glProgramUniform3fEXT");
d->ProgramUniform4fEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w))context->getProcAddress("glProgramUniform4fEXT");
d->ProgramUniform1ivEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, const GLint *value))context->getProcAddress("glProgramUniform1ivEXT");
d->ProgramUniform2ivEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, const GLint *value))context->getProcAddress("glProgramUniform2ivEXT");
d->ProgramUniform3ivEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, const GLint *value))context->getProcAddress("glProgramUniform3ivEXT");
d->ProgramUniform4ivEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, const GLint *value))context->getProcAddress("glProgramUniform4ivEXT");
d->ProgramUniform1fvEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, const GLfloat *value))context->getProcAddress("glProgramUniform1fvEXT");
d->ProgramUniform2fvEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, const GLfloat *value))context->getProcAddress("glProgramUniform2fvEXT");
d->ProgramUniform3fvEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, const GLfloat *value))context->getProcAddress("glProgramUniform3fvEXT");
d->ProgramUniform4fvEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, const GLfloat *value))context->getProcAddress("glProgramUniform4fvEXT");
d->ProgramUniformMatrix2fvEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value))context->getProcAddress("glProgramUniformMatrix2fvEXT");
d->ProgramUniformMatrix3fvEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value))context->getProcAddress("glProgramUniformMatrix3fvEXT");
d->ProgramUniformMatrix4fvEXT = (void (QOPENGLF_APIENTRYP)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value))context->getProcAddress("glProgramUniformMatrix4fvEXT");
d->ValidateProgramPipelineEXT = (void (QOPENGLF_APIENTRYP)(GLuint pipeline))context->getProcAddress("glValidateProgramPipelineEXT");
d->GetProgramPipelineInfoLogEXT = (void (QOPENGLF_APIENTRYP)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog))context->getProcAddress("glGetProgramPipelineInfoLogEXT");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_EXT_texture_storage::QOpenGLExtension_EXT_texture_storage()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_EXT_texture_storagePrivate))
{
}
bool QOpenGLExtension_EXT_texture_storage::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_EXT_texture_storage);
d->TexStorage1DEXT = (void (QOPENGLF_APIENTRYP)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width))context->getProcAddress("glTexStorage1DEXT");
d->TexStorage2DEXT = (void (QOPENGLF_APIENTRYP)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height))context->getProcAddress("glTexStorage2DEXT");
d->TexStorage3DEXT = (void (QOPENGLF_APIENTRYP)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth))context->getProcAddress("glTexStorage3DEXT");
d->TextureStorage1DEXT = (void (QOPENGLF_APIENTRYP)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width))context->getProcAddress("glTextureStorage1DEXT");
d->TextureStorage2DEXT = (void (QOPENGLF_APIENTRYP)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height))context->getProcAddress("glTextureStorage2DEXT");
d->TextureStorage3DEXT = (void (QOPENGLF_APIENTRYP)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth))context->getProcAddress("glTextureStorage3DEXT");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_IMG_multisampled_render_to_texture::QOpenGLExtension_IMG_multisampled_render_to_texture()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_IMG_multisampled_render_to_texturePrivate))
{
}
bool QOpenGLExtension_IMG_multisampled_render_to_texture::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_IMG_multisampled_render_to_texture);
d->RenderbufferStorageMultisampleIMG = (void (QOPENGLF_APIENTRYP)(GLenum, GLsizei, GLenum, GLsizei, GLsizei))context->getProcAddress("glRenderbufferStorageMultisampleIMG");
d->FramebufferTexture2DMultisampleIMG = (void (QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei))context->getProcAddress("glFramebufferTexture2DMultisampleIMG");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_NV_coverage_sample::QOpenGLExtension_NV_coverage_sample()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_NV_coverage_samplePrivate))
{
}
bool QOpenGLExtension_NV_coverage_sample::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_NV_coverage_sample);
d->CoverageMaskNV = (void (QOPENGLF_APIENTRYP)(GLboolean mask))context->getProcAddress("glCoverageMaskNV");
d->CoverageOperationNV = (void (QOPENGLF_APIENTRYP)(GLenum operation))context->getProcAddress("glCoverageOperationNV");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_NV_draw_buffers::QOpenGLExtension_NV_draw_buffers()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_NV_draw_buffersPrivate))
{
}
bool QOpenGLExtension_NV_draw_buffers::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_NV_draw_buffers);
d->DrawBuffersNV = (void (QOPENGLF_APIENTRYP)(GLsizei n, const GLenum *bufs))context->getProcAddress("glDrawBuffersNV");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_NV_fence::QOpenGLExtension_NV_fence()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_NV_fencePrivate))
{
}
bool QOpenGLExtension_NV_fence::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_NV_fence);
d->DeleteFencesNV = (void (QOPENGLF_APIENTRYP)(GLsizei n, const GLuint *fences))context->getProcAddress("glDeleteFencesNV");
d->GenFencesNV = (void (QOPENGLF_APIENTRYP)(GLsizei n, GLuint *fences))context->getProcAddress("glGenFencesNV");
d->IsFenceNV = (GLboolean (QOPENGLF_APIENTRYP)(GLuint fence))context->getProcAddress("glIsFenceNV");
d->TestFenceNV = (GLboolean (QOPENGLF_APIENTRYP)(GLuint fence))context->getProcAddress("glTestFenceNV");
d->GetFenceivNV = (void (QOPENGLF_APIENTRYP)(GLuint fence, GLenum pname, GLint *params))context->getProcAddress("glGetFenceivNV");
d->FinishFenceNV = (void (QOPENGLF_APIENTRYP)(GLuint fence))context->getProcAddress("glFinishFenceNV");
d->SetFenceNV = (void (QOPENGLF_APIENTRYP)(GLuint fence, GLenum condition))context->getProcAddress("glSetFenceNV");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_NV_read_buffer::QOpenGLExtension_NV_read_buffer()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_NV_read_bufferPrivate))
{
}
bool QOpenGLExtension_NV_read_buffer::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_NV_read_buffer);
d->ReadBufferNV = (void (QOPENGLF_APIENTRYP)(GLenum mode))context->getProcAddress("glReadBufferNV");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_QCOM_alpha_test::QOpenGLExtension_QCOM_alpha_test()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_QCOM_alpha_testPrivate))
{
}
bool QOpenGLExtension_QCOM_alpha_test::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_QCOM_alpha_test);
d->AlphaFuncQCOM = (void (QOPENGLF_APIENTRYP )(GLenum func, GLclampf ref))context->getProcAddress("glAlphaFuncQCOM");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_QCOM_driver_control::QOpenGLExtension_QCOM_driver_control()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_QCOM_driver_controlPrivate))
{
}
bool QOpenGLExtension_QCOM_driver_control::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_QCOM_driver_control);
d->GetDriverControlsQCOM = (void (QOPENGLF_APIENTRYP)(GLint *num, GLsizei size, GLuint *driverControls))context->getProcAddress("glGetDriverControlsQCOM");
d->GetDriverControlStringQCOM = (void (QOPENGLF_APIENTRYP)(GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString))context->getProcAddress("glGetDriverControlStringQCOM");
d->EnableDriverControlQCOM = (void (QOPENGLF_APIENTRYP)(GLuint driverControl))context->getProcAddress("glEnableDriverControlQCOM");
d->DisableDriverControlQCOM = (void (QOPENGLF_APIENTRYP)(GLuint driverControl))context->getProcAddress("glDisableDriverControlQCOM");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_QCOM_extended_get::QOpenGLExtension_QCOM_extended_get()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_QCOM_extended_getPrivate))
{
}
bool QOpenGLExtension_QCOM_extended_get::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_QCOM_extended_get);
d->ExtGetTexturesQCOM = (void (QOPENGLF_APIENTRYP)(GLuint *textures, GLint maxTextures, GLint *numTextures))context->getProcAddress("glExtGetTexturesQCOM");
d->ExtGetBuffersQCOM = (void (QOPENGLF_APIENTRYP)(GLuint *buffers, GLint maxBuffers, GLint *numBuffers))context->getProcAddress("glExtGetBuffersQCOM");
d->ExtGetRenderbuffersQCOM = (void (QOPENGLF_APIENTRYP)(GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers))context->getProcAddress("glExtGetRenderbuffersQCOM");
d->ExtGetFramebuffersQCOM = (void (QOPENGLF_APIENTRYP)(GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers))context->getProcAddress("glExtGetFramebuffersQCOM");
d->ExtGetTexLevelParameterivQCOM = (void (QOPENGLF_APIENTRYP)(GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params))context->getProcAddress("glExtGetTexLevelParameterivQCOM");
d->ExtTexObjectStateOverrideiQCOM = (void (QOPENGLF_APIENTRYP)(GLenum target, GLenum pname, GLint param))context->getProcAddress("glExtTexObjectStateOverrideiQCOM");
d->ExtGetTexSubImageQCOM = (void (QOPENGLF_APIENTRYP)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels))context->getProcAddress("glExtGetTexSubImageQCOM");
d->ExtGetBufferPointervQCOM = (void (QOPENGLF_APIENTRYP)(GLenum target, GLvoid **params))context->getProcAddress("glExtGetBufferPointervQCOM");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_QCOM_extended_get2::QOpenGLExtension_QCOM_extended_get2()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_QCOM_extended_get2Private))
{
}
bool QOpenGLExtension_QCOM_extended_get2::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_QCOM_extended_get2);
d->ExtGetShadersQCOM = (void (QOPENGLF_APIENTRYP)(GLuint *shaders, GLint maxShaders, GLint *numShaders))context->getProcAddress("glExtGetShadersQCOM");
d->ExtGetProgramsQCOM = (void (QOPENGLF_APIENTRYP)(GLuint *programs, GLint maxPrograms, GLint *numPrograms))context->getProcAddress("glExtGetProgramsQCOM");
d->ExtIsProgramBinaryQCOM = (GLboolean (QOPENGLF_APIENTRYP)(GLuint program))context->getProcAddress("glExtIsProgramBinaryQCOM");
d->ExtGetProgramBinarySourceQCOM = (void (QOPENGLF_APIENTRYP)(GLuint program, GLenum shadertype, GLchar *source, GLint *length))context->getProcAddress("glExtGetProgramBinarySourceQCOM");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
QOpenGLExtension_QCOM_tiled_rendering::QOpenGLExtension_QCOM_tiled_rendering()
: QAbstractOpenGLExtension(*(new QOpenGLExtension_QCOM_tiled_renderingPrivate))
{
}
bool QOpenGLExtension_QCOM_tiled_rendering::initializeOpenGLFunctions()
{
if (isInitialized())
return true;
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("A current OpenGL context is required to resolve OpenGL extension functions");
return false;
}
// Resolve the functions
Q_D(QOpenGLExtension_QCOM_tiled_rendering);
d->StartTilingQCOM = (void (QOPENGLF_APIENTRYP)(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask))context->getProcAddress("glStartTilingQCOM");
d->EndTilingQCOM = (void (QOPENGLF_APIENTRYP)(GLbitfield preserveMask))context->getProcAddress("glEndTilingQCOM");
return QAbstractOpenGLExtension::initializeOpenGLFunctions();
}
#endif
QT_END_NAMESPACE

View File

@ -0,0 +1,38 @@
// Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/***************************************************************************
** This file was generated by glgen version 0.1
** Command line was: glgen
**
** glgen is Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
**
** This is an auto-generated file.
** Do not edit! All changes made to it will be lost.
**
****************************************************************************/
#include "qopenglextensions.h"
#include <QtGui/qopenglcontext.h>
QT_BEGIN_NAMESPACE
QAbstractOpenGLExtension::~QAbstractOpenGLExtension()
{
if (d_ptr)
delete d_ptr;
}
bool QAbstractOpenGLExtension::initializeOpenGLFunctions()
{
Q_D(QAbstractOpenGLExtension);
d->initialized = true;
return true;
}
bool QAbstractOpenGLExtension::isInitialized() const
{
Q_D(const QAbstractOpenGLExtension);
return d->initialized;
}
#if !QT_CONFIG(opengles2)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
// Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/***************************************************************************
** This file was generated by glgen version 0.1
** Command line was: glgen
**
** glgen is Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
**
** This is an auto-generated file.
** Do not edit! All changes made to it will be lost.
**
****************************************************************************/
#ifndef QOPENGLEXTENSIONS_H
#define QOPENGLEXTENSIONS_H
#include <QtCore/qglobal.h>
#ifndef QT_NO_OPENGL
#include <QtGui/qopengl.h>
class QOpenGLContext;
QT_BEGIN_NAMESPACE
#if 0
// silence syncqt warnings
#pragma qt_class(QOpenGLExtensions)
#pragma qt_sync_stop_processing
#endif
class QAbstractOpenGLExtensionPrivate
{
public:
QAbstractOpenGLExtensionPrivate() : initialized(false) {}
bool initialized;
};
class QAbstractOpenGLExtension
{
public:
virtual ~QAbstractOpenGLExtension();
virtual bool initializeOpenGLFunctions();
Q_DECLARE_PRIVATE(QAbstractOpenGLExtension)
protected:
bool isInitialized() const;
QAbstractOpenGLExtension() {}
QAbstractOpenGLExtension(QAbstractOpenGLExtensionPrivate &dd) : d_ptr(&dd) {}
QAbstractOpenGLExtensionPrivate *d_ptr;
};
#if !QT_CONFIG(opengles2)

View File

@ -0,0 +1,8 @@
#else
// No backends for OpenGL ES 2
#endif // !QT_CONFIG(opengles2)
QT_END_NAMESPACE

View File

@ -0,0 +1,183 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
/***************************************************************************
** This file was generated by glgen version 0.1
** Command line was: glgen
**
** glgen is Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
**
** This is an auto-generated file.
** Do not edit! All changes made to it will be lost.
**
****************************************************************************/
#include "qopenglversionfunctions.h"
#include "qopenglcontext.h"
#include "qdebug.h"
QT_BEGIN_NAMESPACE
QOpenGLVersionFunctionsBackend *QAbstractOpenGLFunctionsPrivate::functionsBackend(QOpenGLContext *context,
const QOpenGLVersionStatus &v)
{
Q_ASSERT(context);
return context->functionsBackend(v);
}
void QAbstractOpenGLFunctionsPrivate::insertFunctionsBackend(QOpenGLContext *context,
const QOpenGLVersionStatus &v,
QOpenGLVersionFunctionsBackend *backend)
{
Q_ASSERT(context);
context->insertFunctionsBackend(v, backend);
}
void QAbstractOpenGLFunctionsPrivate::removeFunctionsBackend(QOpenGLContext *context, const QOpenGLVersionStatus &v)
{
Q_ASSERT(context);
context->removeFunctionsBackend(v);
}
/*!
\class QAbstractOpenGLFunctions
\inmodule QtGui
\since 5.1
\brief The QAbstractOpenGLFunctions class is the base class of a family of
classes that expose all functions for each OpenGL version and
profile.
OpenGL implementations on different platforms are able to link to a variable
number of OpenGL functions depending upon the OpenGL ABI on that platform.
For example, on Microsoft Windows only functions up to those in OpenGL 1.1
can be linked to at build time. All other functions must be resolved at
runtime. The traditional solution to this has been to use either
QOpenGLContext::getProcAddress() or QOpenGLFunctions. The former is tedious
and error prone and means dealing directly with function pointers. The
latter only exposes those functions common to OpenGL ES 2 and desktop
OpenGL. There is however much new OpenGL functionality that is useful when
writing real world OpenGL applications.
Qt now provides a family of classes which all inherit from
QAbstractOpenGLFunctions which expose every core OpenGL function by way of a
corresponding member function. There is a class for every valid combination
of OpenGL version and profile. Each class follows the naming convention
QOpenGLFunctions_<MAJOR VERSION>_<MINOR VERSION>[_PROFILE].
For OpenGL versions 1.0 through to 3.0 there are no profiles, leading to the
classes:
\list
\li QOpenGLFunctions_1_0
\li QOpenGLFunctions_1_1
\li QOpenGLFunctions_1_2
\li QOpenGLFunctions_1_3
\li QOpenGLFunctions_1_4
\li QOpenGLFunctions_1_5
\li QOpenGLFunctions_2_0
\li QOpenGLFunctions_2_1
\li QOpenGLFunctions_3_0
\endlist
where each class inherits from QAbstractOpenGLFunctions.
OpenGL version 3.1 removed many deprecated functions leading to a much
simpler and generic API.
With OpenGL 3.2 the concept of profiles was introduced. Two profiles are
currently defined for OpenGL: Core and Compatibility.
The Core profile does not include any of the functions that were removed
in OpenGL 3.1. The Compatibility profile contains all functions in the
Core profile of the same version plus all of the functions that were
removed in OpenGL 3.1. In this way the Compatibility profile classes allow
use of newer OpenGL functionality but also allows you to keep using your
legacy OpenGL code. For new OpenGL code the Core profile should be
preferred.
Please note that some vendors, notably Apple, do not implement the
Compatibility profile. Therefore if you wish to target new OpenGL features
on OS X then you should ensure that you request a Core profile context via
QSurfaceFormat::setProfile().
Qt provides classes for all version and Core and Compatibility profile
combinations. The classes for OpenGL versions 3.1 through to 4.3 are:
\list
\li QOpenGLFunctions_3_1
\li QOpenGLFunctions_3_2_Core
\li QOpenGLFunctions_3_2_Compatibility
\li QOpenGLFunctions_3_3_Core
\li QOpenGLFunctions_3_3_Compatibility
\li QOpenGLFunctions_4_0_Core
\li QOpenGLFunctions_4_0_Compatibility
\li QOpenGLFunctions_4_1_Core
\li QOpenGLFunctions_4_1_Compatibility
\li QOpenGLFunctions_4_2_Core
\li QOpenGLFunctions_4_2_Compatibility
\li QOpenGLFunctions_4_3_Core
\li QOpenGLFunctions_4_3_Compatibility
\endlist
where each class inherits from QAbstractOpenGLFunctions.
A pointer to an object of the class corresponding to the version and
profile of OpenGL in use can be obtained from
QOpenGLFunctions::versionFunctions(). If obtained in this way, note that
the QOpenGLContext retains ownership of the object. This is so that only
one instance need be created.
Before calling any of the exposed OpenGL functions you must ensure that the
object has resolved the function pointers to the OpenGL functions. This
only needs to be done once per instance with initializeOpenGLFunctions().
Once initialized, the object can be used to call any OpenGL function for
the corresponding version and profile. Note that initializeOpenGLFunctions()
can fail in some circumstances so check the return value. Situations in
which initialization can fail are if you have a functions object for a version
or profile that contains functions that are not part of the context being
used to resolve the function pointers.
If you exclusively use function objects then you will get compile time
errors if you attempt to use a function not included in that version and
profile. This is obviously a lot easier to debug than undefined behavior
at run time.
\sa QOpenGLContext::versionFunctions()
*/
QAbstractOpenGLFunctions::QAbstractOpenGLFunctions()
: d_ptr(new QAbstractOpenGLFunctionsPrivate)
{
}
QAbstractOpenGLFunctions::~QAbstractOpenGLFunctions()
{
delete d_ptr;
}
bool QAbstractOpenGLFunctions::initializeOpenGLFunctions()
{
Q_D(QAbstractOpenGLFunctions);
d->initialized = true;
return true;
}
bool QAbstractOpenGLFunctions::isInitialized() const
{
Q_D(const QAbstractOpenGLFunctions);
return d->initialized;
}
void QAbstractOpenGLFunctions::setOwningContext(const QOpenGLContext *context)
{
Q_D(QAbstractOpenGLFunctions);
d->owningContext = const_cast<QOpenGLContext*>(context);
}
QOpenGLContext *QAbstractOpenGLFunctions::owningContext() const
{
Q_D(const QAbstractOpenGLFunctions);
return d->owningContext;
}
#if !QT_CONFIG(opengles2)

View File

@ -0,0 +1,13 @@
#else
// No need for backend classes with function pointers with ES2.
// All function addresses are independent of context and display.
#endif // !QT_CONFIG(opengles2)
QT_END_NAMESPACE
#endif // QT_NO_OPENGL
#endif

View File

@ -0,0 +1,127 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
/***************************************************************************
** This file was generated by glgen version 0.1
** Command line was: glgen
**
** glgen is Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
**
** This is an auto-generated file.
** Do not edit! All changes made to it will be lost.
**
****************************************************************************/
#ifndef QOPENGLVERSIONFUNCTIONS_H
#define QOPENGLVERSIONFUNCTIONS_H
#include <QtCore/qglobal.h>
#ifndef QT_NO_OPENGL
#include <QtCore/qhash.h>
#include <QtCore/qpair.h>
#include <QtGui/qopengl.h>
QT_BEGIN_NAMESPACE
class QOpenGLContext;
#if 0
// silence syncqt warnings
#pragma qt_class(QOpenGLVersionFunctions)
#pragma qt_sync_stop_processing
#endif
struct QOpenGLVersionStatus
{
enum OpenGLStatus {
CoreStatus,
DeprecatedStatus,
InvalidStatus
};
QOpenGLVersionStatus()
: version(qMakePair(0, 0)),
status(InvalidStatus)
{}
QOpenGLVersionStatus(int majorVersion, int minorVersion, QOpenGLVersionStatus::OpenGLStatus functionStatus)
: version(qMakePair(majorVersion, minorVersion)),
status(functionStatus)
{}
QPair<int, int> version;
OpenGLStatus status;
};
inline size_t qHash(const QOpenGLVersionStatus &v, size_t seed)
{
return qHash(static_cast<int>(v.status * 1000)
+ v.version.first * 100 + v.version.second * 10, seed);
}
inline bool operator==(const QOpenGLVersionStatus &lhs, const QOpenGLVersionStatus &rhs)
{
if (lhs.status != rhs.status)
return false;
return lhs.version == rhs.version;
}
inline bool operator!=(const QOpenGLVersionStatus &lhs, const QOpenGLVersionStatus &rhs)
{
return !operator==(lhs, rhs);
}
class QOpenGLVersionFunctionsBackend
{
public:
QOpenGLVersionFunctionsBackend(QOpenGLContext *ctx)
: context(ctx)
{}
QOpenGLContext *context;
QAtomicInt refs;
};
class QAbstractOpenGLFunctionsPrivate
{
public:
QAbstractOpenGLFunctionsPrivate()
: owningContext(0),
initialized(false)
{}
static QOpenGLVersionFunctionsBackend *functionsBackend(QOpenGLContext *context,
const QOpenGLVersionStatus &v);
static void insertFunctionsBackend(QOpenGLContext *context,
const QOpenGLVersionStatus &v,
QOpenGLVersionFunctionsBackend *backend);
static void removeFunctionsBackend(QOpenGLContext *context, const QOpenGLVersionStatus &v);
QOpenGLContext *owningContext;
bool initialized;
};
class QAbstractOpenGLFunctions
{
public:
virtual ~QAbstractOpenGLFunctions();
virtual bool initializeOpenGLFunctions();
Q_DECLARE_PRIVATE(QAbstractOpenGLFunctions)
protected:
QAbstractOpenGLFunctions();
QAbstractOpenGLFunctionsPrivate *d_ptr;
bool isInitialized() const;
void setOwningContext(const QOpenGLContext *context);
QOpenGLContext *owningContext() const;
friend class QOpenGLContext;
};
#if !QT_CONFIG(opengles2)

View File

@ -0,0 +1,2 @@
QT_END_NAMESPACE

View File

@ -0,0 +1,18 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
/***************************************************************************
** This file was generated by glgen version 0.1
** Command line was: glgen
**
** glgen is Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
**
** This is an auto-generated file.
** Do not edit! All changes made to it will be lost.
**
****************************************************************************/
#include "qopenglfunctions__VERSION__.h"
#include "qopenglcontext.h"
QT_BEGIN_NAMESPACE

View File

@ -0,0 +1,6 @@
QT_END_NAMESPACE
#endif // QT_NO_OPENGL && !QT_CONFIG(opengles2)
#endif

View File

@ -0,0 +1,25 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
/***************************************************************************
** This file was generated by glgen version 0.1
** Command line was: glgen
**
** glgen is Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
**
** This is an auto-generated file.
** Do not edit! All changes made to it will be lost.
**
****************************************************************************/
#ifndef QOPENGLVERSIONFUNCTIONS__VERSION___H
#define QOPENGLVERSIONFUNCTIONS__VERSION___H
#include <QtCore/qglobal.h>
#if !defined(QT_NO_OPENGL) && !QT_CONFIG(opengles2)
#include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h>
QT_BEGIN_NAMESPACE

View File

@ -0,0 +1,2 @@
QT_END_NAMESPACE

View File

@ -0,0 +1,15 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
/***************************************************************************
** This file was generated by glgen version 0.1
** Command line was: glgen
**
** glgen is Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
**
** This is an auto-generated file.
** Do not edit! All changes made to it will be lost.
**
****************************************************************************/
#include "qopenglversionfunctionsfactory_p.h"

View File

@ -0,0 +1,36 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
/***************************************************************************
** This file was generated by glgen version 0.1
** Command line was: glgen
**
** glgen is Copyright (C) 2012 Klaralvdalens Datakonsult AB (KDAB)
**
** This is an auto-generated file.
** Do not edit! All changes made to it will be lost.
**
****************************************************************************/
#ifndef QOPENGLVERSIONFUNCTIONFACTORY_P_H
#define QOPENGLVERSIONFUNCTIONFACTORY_P_H
#ifndef QT_NO_OPENGL
#include <QtCore/QtGlobal>
#include <QtGui/qopenglcontext.h>
QT_BEGIN_NAMESPACE
class QAbstractOpenGLFunctions;
class QOpenGLVersionFunctionsFactory
{
public:
static QAbstractOpenGLFunctions *create(const QOpenGLVersionProfile &versionProfile);
};
QT_END_NAMESPACE
#endif // QT_NO_OPENGL
#endif

224
util/glgen/specparser.h Normal file
View File

@ -0,0 +1,224 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef SPECPARSER_H
#define SPECPARSER_H
#include <QStringList>
#include <QVariant>
class QTextStream;
class QXmlStreamReader;
struct Version {
int major;
int minor;
};
inline bool operator == (const Version &lhs, const Version &rhs)
{
return (lhs.major == rhs.major && lhs.minor == rhs.minor);
}
inline bool operator != (const Version &lhs, const Version &rhs)
{
return !(lhs == rhs);
}
inline bool operator < (const Version &lhs, const Version &rhs)
{
if (lhs.major != rhs.major)
return (lhs.major < rhs.major);
else
return (lhs.minor < rhs.minor);
}
inline bool operator > (const Version &lhs, const Version &rhs)
{
if (lhs.major != rhs.major)
return (lhs.major > rhs.major);
else
return (lhs.minor > rhs.minor);
}
inline bool operator >= (const Version &lhs, const Version &rhs)
{
return !(lhs < rhs);
}
inline bool operator <= (const Version &lhs, const Version &rhs)
{
return !(lhs > rhs);
}
inline size_t qHash(const Version &v)
{
return qHash(v.major * 100 + v.minor * 10);
}
struct VersionProfile
{
enum OpenGLProfile {
CoreProfile = 0,
CompatibilityProfile
};
inline bool hasProfiles() const
{
return ( version.major > 3
|| (version.major == 3 && version.minor > 1));
}
Version version;
OpenGLProfile profile;
};
inline bool operator == (const VersionProfile &lhs, const VersionProfile &rhs)
{
if (lhs.profile != rhs.profile)
return false;
return lhs.version == rhs.version;
}
inline bool operator != (const VersionProfile &lhs, const VersionProfile &rhs)
{
return !(lhs == rhs);
}
inline bool operator < (const VersionProfile &lhs, const VersionProfile &rhs)
{
if (lhs.profile != rhs.profile)
return (lhs.profile < rhs.profile);
return (lhs.version < rhs.version);
}
inline size_t qHash(const VersionProfile &v)
{
return qHash(static_cast<int>(v.profile * 1000) + v.version.major * 100 + v.version.minor * 10);
}
struct Argument
{
enum Direction {
In = 0,
Out
};
enum Mode {
Value = 0,
Array,
Reference
};
QString type;
QString name;
Direction direction;
Mode mode;
};
struct Function
{
QString returnType;
QString name;
QList<Argument> arguments;
};
inline bool operator== (const Argument &lhs, const Argument &rhs)
{
if ((lhs.type != rhs.type) || (lhs.name != rhs.name) || (lhs.direction != rhs.direction)) {
return false;
}
return (lhs.mode != rhs.mode);
}
inline bool operator!= (const Argument &lhs, const Argument &rhs)
{
return !(lhs == rhs);
}
inline bool operator== (const Function &lhs, const Function &rhs)
{
if ((lhs.returnType != rhs.returnType) || (lhs.name != rhs.name)) {
return false;
}
return (lhs.arguments == rhs.arguments);
}
inline bool operator!= (const Function &lhs, const Function &rhs)
{
return !(lhs == rhs);
}
typedef QList<Function> FunctionList;
typedef QMap<VersionProfile, FunctionList> FunctionCollection;
struct FunctionProfile
{
VersionProfile::OpenGLProfile profile;
Function function;
};
class SpecParser
{
public:
virtual ~SpecParser() {}
QString specFileName() const
{
return m_specFileName;
}
QString typeMapFileName() const
{
return m_typeMapFileName;
}
virtual QList<Version> versions() const = 0;
QList<VersionProfile> versionProfiles() const {return versionFunctions().uniqueKeys();}
QList<Function> functionsForVersion(const VersionProfile &v) const
{
return versionFunctions().values(v);
}
QStringList extensions() const
{
return QStringList(extensionFunctions().uniqueKeys());
}
QList<Function> functionsForExtension(const QString &extension)
{
QList<Function> func;
Q_FOREACH (const FunctionProfile &f, extensionFunctions().values(extension))
func.append(f.function);
return func;
}
void setSpecFileName(QString arg)
{
m_specFileName = arg;
}
void setTypeMapFileName(QString arg)
{
m_typeMapFileName = arg;
}
virtual bool parse() = 0;
protected:
virtual const QMultiHash<VersionProfile, Function> &versionFunctions() const = 0;
virtual const QMultiMap<QString, FunctionProfile> &extensionFunctions() const = 0;
private:
QString m_specFileName;
QString m_typeMapFileName;
};
#endif // SPECPARSER_H

View File

@ -0,0 +1,402 @@
// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "xmlspecparser.h"
#include <QDebug>
#include <QFile>
#include <QRegularExpression>
#include <QStringList>
#include <QTextStream>
#include <QXmlStreamReader>
#ifdef SPECPARSER_DEBUG
#define qXmlSpecParserDebug qDebug
#else
#define qXmlSpecParserDebug QT_NO_QDEBUG_MACRO
#endif
bool XmlSpecParser::parse()
{
// Open up a stream on the actual OpenGL function spec file
QFile file(specFileName());
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Failed to open spec file:" << specFileName() << "Aborting";
return false;
}
QXmlStreamReader stream(&file);
// Extract the info that we need
parseFunctions(stream);
return true;
}
void XmlSpecParser::parseParam(QXmlStreamReader &stream, Function &func)
{
Argument arg;
arg.type = QString();
while (!stream.isEndDocument()) {
stream.readNext();
if (stream.isStartElement()) {
QString tag = stream.name().toString();
if (tag == "ptype") {
if (stream.readNext() == QXmlStreamReader::Characters)
arg.type.append(stream.text().toString());
}
else if (tag == "name") {
if (stream.readNext() == QXmlStreamReader::Characters)
arg.name = stream.text().toString().trimmed();
}
} else if (stream.isCharacters()) {
arg.type.append(stream.text().toString());
} else if (stream.isEndElement()) {
QString tag = stream.name().toString();
if (tag == "param") {
// compatibility with old spec
QRegularExpression typeRegExp("(const )?(.+)(?<!\\*)((?:(?!\\*$)\\*)*)(\\*)?");
// remove extra whitespace
arg.type = arg.type.trimmed();
// set default
arg.direction = Argument::In;
arg.mode = Argument::Value;
QRegularExpressionMatch exp = typeRegExp.match(arg.type);
if (exp.hasMatch()) {
if (!exp.captured(4).isEmpty()) {
arg.mode = Argument::Reference;
if (exp.captured(1).isEmpty())
arg.direction = Argument::Out;
}
arg.type = exp.captured(2) + exp.captured(3);
}
break;
}
}
}
// remove any excess whitespace
arg.type = arg.type.trimmed();
arg.name = arg.name.trimmed();
// maybe some checks?
func.arguments.append(arg);
}
void XmlSpecParser::parseCommand(QXmlStreamReader &stream)
{
Function func;
while (!stream.isEndDocument()) {
stream.readNext();
if (stream.isStartElement()) {
QString tag = stream.name().toString();
if (tag == "proto") {
while (!stream.isEndDocument()) {
stream.readNext();
if (stream.isStartElement() && (stream.name().toString() == "name")) {
if (stream.readNext() == QXmlStreamReader::Characters)
func.name = stream.text().toString();
} else if (stream.isCharacters()) {
func.returnType.append(stream.text().toString());
} else if (stream.isEndElement() && (stream.name().toString() == "proto")) {
break;
}
}
}
if (tag == "param")
parseParam(stream, func);
}
else if (stream.isEndElement()) {
QString tag = stream.name().toString();
if (tag == "command")
break;
}
}
// maybe checks?
func.returnType = func.returnType.trimmed();
// for compatibility with old spec
if (func.name.startsWith("gl"))
func.name.remove(0, 2);
m_functionList.insert(func.name, func);
}
void XmlSpecParser::parseCommands(QXmlStreamReader &stream)
{
while (!stream.isEndDocument()) {
stream.readNext();
if (stream.isStartElement()) {
QString tag = stream.name().toString();
if (tag == "command")
parseCommand(stream);
}
else if (stream.isEndElement()) {
QString tag = stream.name().toString();
if (tag == "commands")
break;
}
}
}
void XmlSpecParser::parseRequire(QXmlStreamReader &stream, FunctionList &funcs)
{
while (!stream.isEndDocument()) {
stream.readNext();
if (stream.isStartElement()) {
QString tag = stream.name().toString();
if (tag == "command") {
QString func = stream.attributes().value("name").toString();
// for compatibility with old spec
if (func.startsWith("gl"))
func.remove(0, 2);
funcs.append(m_functionList[func]);
}
} else if (stream.isEndElement()) {
QString tag = stream.name().toString();
if (tag == "require")
break;
}
}
}
void XmlSpecParser::parseRemoveCore(QXmlStreamReader &stream)
{
while (!stream.isEndDocument()) {
stream.readNext();
if (stream.isStartElement()) {
QString tag = stream.name().toString();
if (tag == "command") {
QString func = stream.attributes().value("name").toString();
// for compatibility with old spec
if (func.startsWith("gl"))
func.remove(0, 2);
// go through list of version and mark as incompatible
Q_FOREACH (const Version& v, m_versions) {
// Combine version and profile for this subset of functions
VersionProfile version;
version.version = v;
version.profile = VersionProfile::CoreProfile;
// Fetch the functions and add to collection for this class
Q_FOREACH (const Function& f, m_functions.values(version)) {
if (f.name == func) {
VersionProfile newVersion;
newVersion.version = v;
newVersion.profile = VersionProfile::CompatibilityProfile;
m_functions.insert(newVersion, f);
m_functions.remove(version, f);
}
}
}
}
} else if (stream.isEndElement()) {
QString tag = stream.name().toString();
if (tag == "remove")
break;
}
}
}
void XmlSpecParser::parseFeature(QXmlStreamReader &stream)
{
QRegularExpression versionRegExp("(\\d).(\\d)");
QXmlStreamAttributes attributes = stream.attributes();
QRegularExpressionMatch match = versionRegExp.match(attributes.value("number").toString());
if (!match.hasMatch()) {
qWarning() << "Malformed version indicator";
return;
}
if (attributes.value("api").toString() != "gl")
return;
int majorVersion = match.captured(1).toInt();
int minorVersion = match.captured(2).toInt();
Version v;
VersionProfile core, compat;
v.major = majorVersion;
v.minor = minorVersion;
core.version = compat.version = v;
core.profile = VersionProfile::CoreProfile;
compat.profile = VersionProfile::CompatibilityProfile;
while (!stream.isEndDocument()) {
stream.readNext();
if (stream.isStartElement()) {
QString tag = stream.name().toString();
if (tag == "require") {
FunctionList funcs;
if (stream.attributes().value("profile").toString() == "compatibility") {
parseRequire(stream, funcs);
Q_FOREACH (const Function& f, funcs) {
m_functions.insert(compat, f);
}
} else {
parseRequire(stream, funcs);
Q_FOREACH (const Function& f, funcs) {
m_functions.insert(core, f);
}
}
} else if (tag == "remove") {
if (stream.attributes().value("profile").toString() == "core")
parseRemoveCore(stream);
}
} else if (stream.isEndElement()) {
QString tag = stream.name().toString();
if (tag == "feature")
break;
}
}
m_versions.append(v);
}
void XmlSpecParser::parseExtension(QXmlStreamReader &stream)
{
QXmlStreamAttributes attributes = stream.attributes();
QString name = attributes.value("name").toString();
while (!stream.isEndDocument()) {
stream.readNext();
if (stream.isStartElement()) {
QString tag = stream.name().toString();
if (tag == "require") {
if (stream.attributes().value("profile").toString() == "compatibility") {
FunctionList funcs;
parseRequire(stream, funcs);
Q_FOREACH (const Function& f, funcs) {
FunctionProfile fp;
fp.function = f;
fp.profile = VersionProfile::CompatibilityProfile;
m_extensionFunctions.insert(name, fp);
}
} else {
FunctionList funcs;
parseRequire(stream, funcs);
Q_FOREACH (const Function& f, funcs) {
FunctionProfile fp;
fp.function = f;
fp.profile = VersionProfile::CoreProfile;
m_extensionFunctions.insert(name, fp);
}
}
}
} else if (stream.isEndElement()) {
QString tag = stream.name().toString();
if (tag == "extension")
break;
}
}
}
void XmlSpecParser::parseFunctions(QXmlStreamReader &stream)
{
while (!stream.isEndDocument()) {
stream.readNext();
if (stream.isStartElement()) {
QString tag = stream.name().toString();
if (tag == "feature") {
parseFeature(stream);
} else if (tag == "commands") {
parseCommands(stream);
} else if (tag == "extension") {
parseExtension(stream);
}
} else if (stream.isEndElement()) {
stream.readNext();
}
}
// hack - add GL_ARB_imaging to every version after 1.2 inclusive
Version versionThreshold;
versionThreshold.major = 1;
versionThreshold.minor = 2;
QList<FunctionProfile> funcs = m_extensionFunctions.values("GL_ARB_imaging");
VersionProfile vp;
vp.version = versionThreshold;
Q_FOREACH (const FunctionProfile& fp, funcs) {
vp.profile = fp.profile;
m_functions.insert(vp, fp.function);
}
// now we will prune any duplicates
QSet<QString> funcset;
Q_FOREACH (const Version& v, m_versions) {
// check compatibility first
VersionProfile vp;
vp.version = v;
vp.profile = VersionProfile::CompatibilityProfile;
Q_FOREACH (const Function& f, m_functions.values(vp)) {
// remove duplicate
if (funcset.contains(f.name))
m_functions.remove(vp, f);
funcset.insert(f.name);
}
vp.profile = VersionProfile::CoreProfile;
Q_FOREACH (const Function& f, m_functions.values(vp)) {
// remove duplicate
if (funcset.contains(f.name))
m_functions.remove(vp, f);
funcset.insert(f.name);
}
}
}

View File

@ -0,0 +1,45 @@
// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef XMLSPECPARSER_H
#define XMLSPECPARSER_H
#include "specparser.h"
#include <QStringList>
#include <QVariant>
class QXmlStreamReader;
class XmlSpecParser : public SpecParser
{
public:
virtual QList<Version> versions() const { return m_versions; }
virtual bool parse();
protected:
const QMultiHash<VersionProfile, Function> &versionFunctions() const { return m_functions; }
const QMultiMap<QString, FunctionProfile> &extensionFunctions() const { return m_extensionFunctions; }
private:
void parseFunctions(QXmlStreamReader &stream);
void parseCommands(QXmlStreamReader &stream);
void parseCommand(QXmlStreamReader &stream);
void parseParam(QXmlStreamReader &stream, Function &func);
void parseFeature(QXmlStreamReader &stream);
void parseExtension(QXmlStreamReader &stream);
void parseRequire(QXmlStreamReader &stream, FunctionList& profile);
void parseRemoveCore(QXmlStreamReader &stream);
QMultiHash<VersionProfile, Function> m_functions;
QList<Version> m_versions;
// Extension support
QMultiMap<QString, FunctionProfile> m_extensionFunctions;
QMap<QString, Function> m_functionList;
};
#endif // XMLSPECPARSER_H

Some files were not shown because too many files have changed in this diff Show More