Browse Source
[cmake, frontend] feat: CPMUtil + dependency viewer (#238)
[cmake, frontend] feat: CPMUtil + dependency viewer (#238)
- creates a CPMUtil.cmake module that makes my job 10x easier and removes boilerplate - also lets us generate dependency names/versions at compiletime, thus letting the frontend display each dependency's versions. Signed-off-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/238pull/246/head
No known key found for this signature in database
GPG Key ID: 425ACD2D4830EBC6
24 changed files with 690 additions and 183 deletions
-
39CMakeLists.txt
-
117CMakeModules/CPMUtil.cmake
-
14CMakeModules/DownloadExternals.cmake
-
21CMakeModules/GenerateDepHashes.cmake
-
209externals/CMakeLists.txt
-
15externals/ffmpeg/CMakeLists.txt
-
2src/CMakeLists.txt
-
3src/common/CMakeLists.txt
-
20src/dep_hashes.h.in
-
42src/dynarmic/externals/CMakeLists.txt
-
12src/network/announce_multiplayer_session.cpp
-
2src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
-
4src/video_core/CMakeLists.txt
-
4src/video_core/vulkan_common/vulkan_device.cpp
-
4src/yuzu/CMakeLists.txt
-
2src/yuzu/aboutdialog.ui
-
121src/yuzu/deps_dialog.cpp
-
41src/yuzu/deps_dialog.h
-
166src/yuzu/deps_dialog.ui
-
12src/yuzu/externals/CMakeLists.txt
-
9src/yuzu/main.cpp
-
1src/yuzu/main.h
-
6src/yuzu/main.ui
-
7tools/cpm-hash.sh
@ -0,0 +1,117 @@ |
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project |
|||
# SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
# Created-By: crueter |
|||
# Docs will come at a later date, mostly this is to just reduce boilerplate |
|||
# and some cmake magic to allow for runtime viewing of dependency versions |
|||
|
|||
cmake_minimum_required(VERSION 3.22) |
|||
include(CPM) |
|||
|
|||
function(AddPackage) |
|||
cpm_set_policies() |
|||
|
|||
set(oneValueArgs |
|||
NAME |
|||
VERSION |
|||
REPO |
|||
SHA |
|||
HASH |
|||
KEY |
|||
URL # Only for custom non-GitHub urls |
|||
DOWNLOAD_ONLY |
|||
FIND_PACKAGE_ARGUMENTS |
|||
SYSTEM_PACKAGE |
|||
BUNDLED_PACKAGE |
|||
) |
|||
|
|||
set(multiValueArgs OPTIONS PATCHES) |
|||
|
|||
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") |
|||
|
|||
if (NOT DEFINED PKG_ARGS_NAME) |
|||
message(FATAL_ERROR "CPMUtil: No package name defined") |
|||
endif() |
|||
|
|||
if (NOT DEFINED PKG_ARGS_URL) |
|||
if (DEFINED PKG_ARGS_REPO AND DEFINED PKG_ARGS_SHA) |
|||
set(PKG_GIT_URL https://github.com/${PKG_ARGS_REPO}) |
|||
set(PKG_URL "${PKG_GIT_URL}/archive/${PKG_ARGS_SHA}.zip") |
|||
else() |
|||
message(FATAL_ERROR "CPMUtil: No custom URL and no repository + sha defined") |
|||
endif() |
|||
else() |
|||
set(PKG_URL ${PKG_ARGS_URL}) |
|||
set(PKG_GIT_URL ${PKG_URL}) |
|||
endif() |
|||
|
|||
message(STATUS "CPMUtil: Downloading package ${PKG_ARGS_NAME} from ${PKG_URL}") |
|||
|
|||
if (NOT DEFINED PKG_ARGS_KEY) |
|||
if (DEFINED PKG_ARGS_SHA) |
|||
string(SUBSTRING ${PKG_ARGS_SHA} 0 4 PKG_KEY) |
|||
message(STATUS "CPMUtil: No custom key defined, using ${PKG_KEY} from sha") |
|||
else() |
|||
message(FATAL_ERROR "CPMUtil: No custom key and no commit sha defined") |
|||
endif() |
|||
else() |
|||
set(PKG_KEY ${PKG_ARGS_KEY}) |
|||
endif() |
|||
|
|||
if (DEFINED PKG_ARGS_HASH) |
|||
set(PKG_HASH "SHA512=${PKG_ARGS_HASH}") |
|||
endif() |
|||
|
|||
# Default behavior is bundled |
|||
if (DEFINED PKG_ARGS_SYSTEM_PACKAGE) |
|||
set(CPM_USE_LOCAL_PACKAGES ${PKG_ARGS_SYSTEM_PACKAGE}) |
|||
elseif (DEFINED PKG_ARGS_BUNDLED_PACKAGE) |
|||
if (PKG_ARGS_BUNDLED_PACKAGE) |
|||
set(CPM_USE_LOCAL_PACKAGES OFF) |
|||
else() |
|||
set(CPM_USE_LOCAL_PACKAGES ON) |
|||
endif() |
|||
else() |
|||
set(CPM_USE_LOCAL_PACKAGES OFF) |
|||
endif() |
|||
|
|||
CPMAddPackage( |
|||
NAME ${PKG_ARGS_NAME} |
|||
VERSION ${PKG_ARGS_VERSION} |
|||
URL ${PKG_URL} |
|||
URL_HASH ${PKG_HASH} |
|||
CUSTOM_CACHE_KEY ${PKG_KEY} |
|||
DOWNLOAD_ONLY ${PKG_ARGS_DOWNLOAD_ONLY} |
|||
FIND_PACKAGE_ARGUMENTS ${PKG_ARGS_FIND_PACKAGE_ARGUMENTS} |
|||
|
|||
OPTIONS ${PKG_ARGS_OPTIONS} |
|||
PATCHES ${PKG_ARGS_PATCHES} |
|||
|
|||
${PKG_ARGS_UNPARSED_ARGUMENTS} |
|||
) |
|||
|
|||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${PKG_ARGS_NAME}) |
|||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS ${PKG_GIT_URL}) |
|||
|
|||
if (${PKG_ARGS_NAME}_ADDED) |
|||
if (DEFINED PKG_ARGS_SHA) |
|||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${PKG_ARGS_SHA}) |
|||
elseif(DEFINED PKG_ARGS_VERSION) |
|||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${PKG_ARGS_VERSION}) |
|||
else() |
|||
message(WARNING "CPMUtil: Package ${PKG_ARGS_NAME} has no specified sha or version") |
|||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS "unknown") |
|||
endif() |
|||
else() |
|||
if (DEFINED CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION) |
|||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS "${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION} (system)") |
|||
else() |
|||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS "unknown (system)") |
|||
endif() |
|||
endif() |
|||
|
|||
# pass stuff to parent scope |
|||
set(${PKG_ARGS_NAME}_ADDED "${${PKG_ARGS_NAME}_ADDED}" PARENT_SCOPE) |
|||
set(${PKG_ARGS_NAME}_SOURCE_DIR "${${PKG_ARGS_NAME}_SOURCE_DIR}" PARENT_SCOPE) |
|||
set(${PKG_ARGS_NAME}_BINARY_DIR "${${PKG_ARGS_NAME}_BINARY_DIR}" PARENT_SCOPE) |
|||
endfunction() |
|||
@ -0,0 +1,21 @@ |
|||
# SPDX-FileCopyrightText: 2025 Eden Emulator Project |
|||
# SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
get_property(NAMES GLOBAL PROPERTY CPM_PACKAGE_NAMES) |
|||
get_property(SHAS GLOBAL PROPERTY CPM_PACKAGE_SHAS) |
|||
get_property(URLS GLOBAL PROPERTY CPM_PACKAGE_URLS) |
|||
|
|||
list(LENGTH NAMES DEPS_LENGTH) |
|||
|
|||
list(JOIN NAMES "\",\n\t\"" DEP_NAME_DIRTY) |
|||
set(DEP_NAMES "\t\"${DEP_NAME_DIRTY}\"") |
|||
|
|||
list(JOIN SHAS "\",\n\t\"" DEP_SHAS_DIRTY) |
|||
set(DEP_SHAS "\t\"${DEP_SHAS_DIRTY}\"") |
|||
|
|||
list(JOIN URLS "\",\n\t\"" DEP_URLS_DIRTY) |
|||
set(DEP_URLS "\t\"${DEP_URLS_DIRTY}\"") |
|||
|
|||
configure_file(dep_hashes.h.in dep_hashes.h @ONLY) |
|||
target_sources(common PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/dep_hashes.h) |
|||
target_include_directories(common PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) |
|||
@ -0,0 +1,20 @@ |
|||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
namespace Common { |
|||
|
|||
static const constexpr std::array<const char *, @DEPS_LENGTH@> dep_names = { |
|||
@DEP_NAMES@ |
|||
}; |
|||
|
|||
static const constexpr std::array<const char *, @DEPS_LENGTH@> dep_hashes = { |
|||
@DEP_SHAS@ |
|||
}; |
|||
|
|||
static const constexpr std::array<const char *, @DEPS_LENGTH@> dep_urls = { |
|||
@DEP_URLS@ |
|||
}; |
|||
|
|||
} // namespace Common |
|||
@ -0,0 +1,121 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include "yuzu/deps_dialog.h"
|
|||
#include <QAbstractTextDocumentLayout>
|
|||
#include <QDesktopServices>
|
|||
#include <QIcon>
|
|||
#include <QPainter>
|
|||
#include <QTableWidget>
|
|||
#include <QTextEdit>
|
|||
#include "dep_hashes.h"
|
|||
#include "ui_deps_dialog.h"
|
|||
#include <fmt/ranges.h>
|
|||
|
|||
DepsDialog::DepsDialog(QWidget* parent) |
|||
: QDialog(parent) |
|||
, ui{std::make_unique<Ui::DepsDialog>()} |
|||
{ |
|||
ui->setupUi(this); |
|||
|
|||
constexpr size_t rows = Common::dep_hashes.size(); |
|||
ui->tableDeps->setRowCount(rows); |
|||
|
|||
QStringList labels; |
|||
labels << tr("Dependency") << tr("Version"); |
|||
|
|||
ui->tableDeps->setHorizontalHeaderLabels(labels); |
|||
ui->tableDeps->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); |
|||
ui->tableDeps->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Fixed); |
|||
ui->tableDeps->horizontalHeader()->setMinimumSectionSize(200); |
|||
|
|||
for (size_t i = 0; i < rows; ++i) { |
|||
const std::string name = Common::dep_names.at(i); |
|||
const std::string sha = Common::dep_hashes.at(i); |
|||
const std::string url = Common::dep_urls.at(i); |
|||
|
|||
std::string dependency = fmt::format("<a href=\"{}\">{}</a>", url, name); |
|||
|
|||
QTableWidgetItem *nameItem = new QTableWidgetItem(QString::fromStdString(dependency)); |
|||
QTableWidgetItem *shaItem = new QTableWidgetItem(QString::fromStdString(sha)); |
|||
|
|||
ui->tableDeps->setItem(i, 0, nameItem); |
|||
ui->tableDeps->setItem(i, 1, shaItem); |
|||
} |
|||
|
|||
ui->tableDeps->setItemDelegateForColumn(0, new LinkItemDelegate(this)); |
|||
} |
|||
|
|||
DepsDialog::~DepsDialog() = default; |
|||
|
|||
LinkItemDelegate::LinkItemDelegate(QObject *parent) |
|||
: QStyledItemDelegate(parent) |
|||
{} |
|||
|
|||
void LinkItemDelegate::paint(QPainter *painter, |
|||
const QStyleOptionViewItem &option, |
|||
const QModelIndex &index) const |
|||
{ |
|||
auto options = option; |
|||
initStyleOption(&options, index); |
|||
|
|||
QTextDocument doc; |
|||
QString html = index.data(Qt::DisplayRole).toString(); |
|||
doc.setHtml(html); |
|||
|
|||
options.text.clear(); |
|||
|
|||
painter->save(); |
|||
painter->translate(options.rect.topLeft()); |
|||
doc.drawContents(painter, QRectF(0, 0, options.rect.width(), options.rect.height())); |
|||
painter->restore(); |
|||
} |
|||
|
|||
QSize LinkItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const |
|||
{ |
|||
QStyleOptionViewItem options = option; |
|||
initStyleOption(&options, index); |
|||
|
|||
QTextDocument doc; |
|||
doc.setHtml(options.text); |
|||
doc.setTextWidth(options.rect.width()); |
|||
return QSize(doc.idealWidth(), doc.size().height()); |
|||
} |
|||
|
|||
bool LinkItemDelegate::editorEvent(QEvent *event, |
|||
QAbstractItemModel *model, |
|||
const QStyleOptionViewItem &option, |
|||
const QModelIndex &index) |
|||
{ |
|||
if (event->type() == QEvent::MouseButtonRelease) { |
|||
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); |
|||
if (mouseEvent->button() == Qt::LeftButton) { |
|||
QString html = index.data(Qt::DisplayRole).toString(); |
|||
QTextDocument doc; |
|||
doc.setHtml(html); |
|||
doc.setTextWidth(option.rect.width()); |
|||
|
|||
// this is kinda silly but it werks
|
|||
QAbstractTextDocumentLayout *layout = doc.documentLayout(); |
|||
|
|||
QPoint pos = mouseEvent->pos() - option.rect.topLeft(); |
|||
int charPos = layout->hitTest(pos, Qt::ExactHit); |
|||
|
|||
if (charPos >= 0) { |
|||
QTextCursor cursor(&doc); |
|||
cursor.setPosition(charPos); |
|||
|
|||
QTextCharFormat format = cursor.charFormat(); |
|||
|
|||
if (format.isAnchor()) { |
|||
QString href = format.anchorHref(); |
|||
if (!href.isEmpty()) { |
|||
QDesktopServices::openUrl(QUrl(href)); |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return QStyledItemDelegate::editorEvent(event, model, option, index); |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <QDialog> |
|||
#include <QStyledItemDelegate> |
|||
#include <QTableView> |
|||
#include <memory> |
|||
|
|||
namespace Ui { class DepsDialog; } |
|||
|
|||
class DepsDialog : public QDialog |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
explicit DepsDialog(QWidget *parent); |
|||
~DepsDialog() override; |
|||
|
|||
private: |
|||
std::unique_ptr<Ui::DepsDialog> ui; |
|||
}; |
|||
|
|||
class LinkItemDelegate : public QStyledItemDelegate |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
explicit LinkItemDelegate(QObject *parent = 0); |
|||
|
|||
protected: |
|||
void paint(QPainter *painter, |
|||
const QStyleOptionViewItem &option, |
|||
const QModelIndex &index) const override; |
|||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; |
|||
bool editorEvent(QEvent *event, |
|||
QAbstractItemModel *model, |
|||
const QStyleOptionViewItem &option, |
|||
const QModelIndex &index) override; |
|||
}; |
|||
@ -0,0 +1,166 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>DepsDialog</class> |
|||
<widget class="QDialog" name="DepsDialog"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>701</width> |
|||
<height>500</height> |
|||
</rect> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>Eden Dependencies</string> |
|||
</property> |
|||
<layout class="QVBoxLayout" name="verticalLayout_3"> |
|||
<item> |
|||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1"> |
|||
<item> |
|||
<layout class="QVBoxLayout" name="verticalLayout_2"> |
|||
<item> |
|||
<widget class="QLabel" name="labelLogo"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="maximumSize"> |
|||
<size> |
|||
<width>200</width> |
|||
<height>200</height> |
|||
</size> |
|||
</property> |
|||
<property name="text"> |
|||
<string/> |
|||
</property> |
|||
<property name="pixmap"> |
|||
<pixmap resource="../../dist/qt_themes/default/default.qrc">:/icons/default/256x256/eden.png</pixmap> |
|||
</property> |
|||
<property name="scaledContents"> |
|||
<bool>true</bool> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<spacer name="verticalSpacer"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Orientation::Vertical</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>20</width> |
|||
<height>40</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
</layout> |
|||
</item> |
|||
<item> |
|||
<layout class="QVBoxLayout" name="verticalLayout"> |
|||
<item> |
|||
<widget class="QLabel" name="labelEden"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string><html><head/><body><p><span style=" font-size:28pt;">Eden Dependencies</span></p></body></html></string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QLabel" name="labelInfo"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string><html><head/><body><p>The projects that make Eden possible</p></body></html></string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QTableWidget" name="tableDeps"> |
|||
<property name="editTriggers"> |
|||
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set> |
|||
</property> |
|||
<property name="alternatingRowColors"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="selectionMode"> |
|||
<enum>QAbstractItemView::SelectionMode::NoSelection</enum> |
|||
</property> |
|||
<property name="columnCount"> |
|||
<number>2</number> |
|||
</property> |
|||
<attribute name="horizontalHeaderShowSortIndicator" stdset="0"> |
|||
<bool>false</bool> |
|||
</attribute> |
|||
<attribute name="verticalHeaderVisible"> |
|||
<bool>false</bool> |
|||
</attribute> |
|||
<column/> |
|||
<column/> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</item> |
|||
</layout> |
|||
</item> |
|||
<item> |
|||
<widget class="QDialogButtonBox" name="buttonBox"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Orientation::Horizontal</enum> |
|||
</property> |
|||
<property name="standardButtons"> |
|||
<set>QDialogButtonBox::StandardButton::Ok</set> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<resources> |
|||
<include location="../../dist/qt_themes/default/default.qrc"/> |
|||
</resources> |
|||
<connections> |
|||
<connection> |
|||
<sender>buttonBox</sender> |
|||
<signal>accepted()</signal> |
|||
<receiver>DepsDialog</receiver> |
|||
<slot>accept()</slot> |
|||
<hints> |
|||
<hint type="sourcelabel"> |
|||
<x>20</x> |
|||
<y>20</y> |
|||
</hint> |
|||
<hint type="destinationlabel"> |
|||
<x>20</x> |
|||
<y>20</y> |
|||
</hint> |
|||
</hints> |
|||
</connection> |
|||
<connection> |
|||
<sender>buttonBox</sender> |
|||
<signal>rejected()</signal> |
|||
<receiver>DepsDialog</receiver> |
|||
<slot>reject()</slot> |
|||
<hints> |
|||
<hint type="sourcelabel"> |
|||
<x>20</x> |
|||
<y>20</y> |
|||
</hint> |
|||
<hint type="destinationlabel"> |
|||
<x>20</x> |
|||
<y>20</y> |
|||
</hint> |
|||
</hints> |
|||
</connection> |
|||
</connections> |
|||
</ui> |
|||
@ -1,7 +1,4 @@ |
|||
#!/bin/sh |
|||
|
|||
for i in $@; do |
|||
SUM=`wget -q $i -O - | sha512sum` |
|||
echo "$i" |
|||
echo "URL_HASH SHA512=$SUM" | cut -d " " -f1-2 |
|||
done |
|||
SUM=`wget -q https://github.com/$1/archive/$2.zip -O - | sha512sum` |
|||
echo "$SUM" | cut -d " " -f1 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue