From 0613a492f375714b1f8807839c87858c1a665efa Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Tue, 6 Dec 2022 12:36:26 +0100 Subject: [PATCH] Vcpkg: Initial commit This introduces the vcpkg support plugin. Initial features are: - Options page for defining vcpkg installation location - Search dialog for searching/selecting a package (incl. parsing autotest) - TextEditor that shows search/options toolbuttons - File wizard that creates a vcpkg.json manifest Change-Id: I098784100c869e0bb2ed6f60db4dedad559a142a Reviewed-by: hjk --- src/plugins/CMakeLists.txt | 1 + src/plugins/plugins.qbs | 1 + src/plugins/vcpkg/CMakeLists.txt | 16 ++ src/plugins/vcpkg/Vcpkg.json.in | 30 +++ src/plugins/vcpkg/vcpkg.qbs | 34 +++ src/plugins/vcpkg/vcpkg.qrc | 6 + src/plugins/vcpkg/vcpkg_test.cpp | 110 +++++++++ src/plugins/vcpkg/vcpkg_test.h | 24 ++ src/plugins/vcpkg/vcpkgconstants.h | 14 ++ src/plugins/vcpkg/vcpkgmanifesteditor.cpp | 71 ++++++ src/plugins/vcpkg/vcpkgmanifesteditor.h | 16 ++ src/plugins/vcpkg/vcpkgplugin.cpp | 38 ++++ src/plugins/vcpkg/vcpkgplugin.h | 26 +++ src/plugins/vcpkg/vcpkgsearch.cpp | 214 ++++++++++++++++++ src/plugins/vcpkg/vcpkgsearch.h | 29 +++ src/plugins/vcpkg/vcpkgsettings.cpp | 80 +++++++ src/plugins/vcpkg/vcpkgsettings.h | 30 +++ src/plugins/vcpkg/vcpkgtr.h | 15 ++ .../vcpkg/wizards/manifest/vcpkg.json.tpl | 6 + .../vcpkg/wizards/manifest/wizard.json | 80 +++++++ 20 files changed, 841 insertions(+) create mode 100644 src/plugins/vcpkg/CMakeLists.txt create mode 100644 src/plugins/vcpkg/Vcpkg.json.in create mode 100644 src/plugins/vcpkg/vcpkg.qbs create mode 100644 src/plugins/vcpkg/vcpkg.qrc create mode 100644 src/plugins/vcpkg/vcpkg_test.cpp create mode 100644 src/plugins/vcpkg/vcpkg_test.h create mode 100644 src/plugins/vcpkg/vcpkgconstants.h create mode 100644 src/plugins/vcpkg/vcpkgmanifesteditor.cpp create mode 100644 src/plugins/vcpkg/vcpkgmanifesteditor.h create mode 100644 src/plugins/vcpkg/vcpkgplugin.cpp create mode 100644 src/plugins/vcpkg/vcpkgplugin.h create mode 100644 src/plugins/vcpkg/vcpkgsearch.cpp create mode 100644 src/plugins/vcpkg/vcpkgsearch.h create mode 100644 src/plugins/vcpkg/vcpkgsettings.cpp create mode 100644 src/plugins/vcpkg/vcpkgsettings.h create mode 100644 src/plugins/vcpkg/vcpkgtr.h create mode 100644 src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl create mode 100644 src/plugins/vcpkg/wizards/manifest/wizard.json diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 5e3ff9bb18a..02f13199140 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -27,6 +27,7 @@ add_subdirectory(help) add_subdirectory(resourceeditor) add_subdirectory(nim) add_subdirectory(conan) +add_subdirectory(vcpkg) # Level 4: (only depends on Level 3 and below) add_subdirectory(classview) diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs index d28aeb05f17..8e57311d9fe 100644 --- a/src/plugins/plugins.qbs +++ b/src/plugins/plugins.qbs @@ -84,6 +84,7 @@ Project { "todo/todo.qbs", "updateinfo/updateinfo.qbs", "valgrind/valgrind.qbs", + "vcpkg/vcpkg.qbs", "vcsbase/vcsbase.qbs", "webassembly/webassembly.qbs", "welcome/welcome.qbs", diff --git a/src/plugins/vcpkg/CMakeLists.txt b/src/plugins/vcpkg/CMakeLists.txt new file mode 100644 index 00000000000..f5d65cf3937 --- /dev/null +++ b/src/plugins/vcpkg/CMakeLists.txt @@ -0,0 +1,16 @@ +add_qtc_plugin(Vcpkg + PLUGIN_DEPENDS Core ProjectExplorer + SOURCES + vcpkg.qrc + vcpkgconstants.h + vcpkgmanifesteditor.cpp vcpkgmanifesteditor.h + vcpkgplugin.cpp vcpkgplugin.h + vcpkgsearch.cpp vcpkgsearch.h + vcpkgsettings.cpp vcpkgsettings.h +) + +extend_qtc_plugin(Vcpkg + CONDITION WITH_TESTS + SOURCES + vcpkg_test.cpp vcpkg_test.h +) diff --git a/src/plugins/vcpkg/Vcpkg.json.in b/src/plugins/vcpkg/Vcpkg.json.in new file mode 100644 index 00000000000..7357c9e845b --- /dev/null +++ b/src/plugins/vcpkg/Vcpkg.json.in @@ -0,0 +1,30 @@ +{ + \"Name\" : \"Vcpkg\", + \"Version\" : \"$$QTCREATOR_VERSION\", + \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", + \"Vendor\" : \"The Qt Company Ltd\", + \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", + \"License\" : [ \"Commercial Usage\", + \"\", + \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\", + \"\", + \"GNU General Public License Usage\", + \"\", + \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\" + ], + \"Experimental\" : true, + \"Description\" : \"vcpkg integration.\", + \"Url\" : \"http://www.qt.io\", + $$dependencyList, + + \"Mimetypes\" : [ + \"\", + \"\", + \" \", + \" \", + \" Vcpkg Manifest File\", + \" \", + \" \", + \"\" + ] +} diff --git a/src/plugins/vcpkg/vcpkg.qbs b/src/plugins/vcpkg/vcpkg.qbs new file mode 100644 index 00000000000..9ab32702090 --- /dev/null +++ b/src/plugins/vcpkg/vcpkg.qbs @@ -0,0 +1,34 @@ +import qbs 1.0 + +QtcPlugin { + name: "Vcpkg" + + Depends { name: "Qt.widgets" } + Depends { name: "Utils" } + + Depends { name: "Core" } + Depends { name: "ProjectExplorer" } + Depends { name: "TextEditor" } + + files: [ + "vcpkg.qrc", + "vcpkgconstants.h", + "vcpkgmanifesteditor.cpp", + "vcpkgmanifesteditor.h", + "vcpkgplugin.cpp", + "vcpkgplugin.h", + "vcpkgsearch.cpp", + "vcpkgsearch.h", + "vcpkgsettings.cpp", + "vcpkgsettings.h", + ] + + Group { + name: "Unit tests" + condition: qtc.testsEnabled + files: [ + "vcpkg_test.h", + "vcpkg_test.cpp", + ] + } +} diff --git a/src/plugins/vcpkg/vcpkg.qrc b/src/plugins/vcpkg/vcpkg.qrc new file mode 100644 index 00000000000..a377b736db3 --- /dev/null +++ b/src/plugins/vcpkg/vcpkg.qrc @@ -0,0 +1,6 @@ + + + wizards/manifest/vcpkg.json.tpl + wizards/manifest/wizard.json + + diff --git a/src/plugins/vcpkg/vcpkg_test.cpp b/src/plugins/vcpkg/vcpkg_test.cpp new file mode 100644 index 00000000000..65079b96a4e --- /dev/null +++ b/src/plugins/vcpkg/vcpkg_test.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "vcpkg_test.h" + +#include "vcpkgsearch.h" + +#include + +namespace Vcpkg::Internal { + +VcpkgSearchTest::VcpkgSearchTest(QObject *parent) + : QObject(parent) +{ } + +VcpkgSearchTest::~VcpkgSearchTest() = default; + +void VcpkgSearchTest::testVcpkgJsonParser_data() +{ + QTest::addColumn("vcpkgManifestJsonData"); + QTest::addColumn("name"); + QTest::addColumn("version"); + QTest::addColumn("license"); + QTest::addColumn("shortDescription"); + QTest::addColumn("description"); + QTest::addColumn("homepage"); + QTest::addColumn("success"); + + QTest::newRow("cimg, version, short description") + << R"({ + "name": "cimg", + "version": "2.9.9", + "description": "The CImg Library is a small, open-source, and modern C++ toolkit for image processing", + "homepage": "https://github.com/dtschump/CImg", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + } + ] + })" + << "cimg" + << "2.9.9" + << "" + << "The CImg Library is a small, open-source, and modern C++ toolkit for image processing" + << QStringList() + << QUrl::fromUserInput("https://github.com/dtschump/CImg") + << true; + + QTest::newRow("catch-classic, version-string, complete description") + << R"({ + "name": "catch-classic", + "version-string": "1.12.2", + "port-version": 1, + "description": [ + "A modern, header-only test framework for unit tests", + "This is specifically the legacy 1.x branch provided for compatibility", + "with older compilers." + ], + "homepage": "https://github.com/catchorg/Catch2" + })" + << "catch-classic" + << "1.12.2" + << "" + << "A modern, header-only test framework for unit tests" + << QStringList({"This is specifically the legacy 1.x branch provided for compatibility", + "with older compilers."}) + << QUrl::fromUserInput("https://github.com/catchorg/Catch2") + << true; + + QTest::newRow("Incomplete") + << R"({ + "version-semver": "1.0", + "description": "foo", + "license": "WTFPL" + })" + << "" + << "1.0" + << "WTFPL" + << "foo" + << QStringList() + << QUrl() + << false; +} + +void VcpkgSearchTest::testVcpkgJsonParser() +{ + QFETCH(QString, vcpkgManifestJsonData); + QFETCH(QString, name); + QFETCH(QString, version); + QFETCH(QString, license); + QFETCH(QString, shortDescription); + QFETCH(QStringList, description); + QFETCH(QUrl, homepage); + QFETCH(bool, success); + + bool ok = false; + const Search::VcpkgManifest mf = + Search::parseVcpkgManifest(vcpkgManifestJsonData.toUtf8(), &ok); + + QCOMPARE(mf.name, name); + QCOMPARE(mf.version, version); + QCOMPARE(mf.license, license); + QCOMPARE(mf.shortDescription, shortDescription); + QCOMPARE(mf.description, description); + QCOMPARE(mf.homepage, homepage); + QCOMPARE(ok, success); +} + +} // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkg_test.h b/src/plugins/vcpkg/vcpkg_test.h new file mode 100644 index 00000000000..8175e28d594 --- /dev/null +++ b/src/plugins/vcpkg/vcpkg_test.h @@ -0,0 +1,24 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +namespace Vcpkg::Internal { + +class VcpkgSearchTest : public QObject +{ + Q_OBJECT + +public: + VcpkgSearchTest(QObject *parent = nullptr); + ~VcpkgSearchTest(); + +private slots: + void testVcpkgJsonParser_data(); + void testVcpkgJsonParser(); +}; + +} // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgconstants.h b/src/plugins/vcpkg/vcpkgconstants.h new file mode 100644 index 00000000000..644dfab95f9 --- /dev/null +++ b/src/plugins/vcpkg/vcpkgconstants.h @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +namespace Vcpkg::Constants { + +const char TOOLSSETTINGSPAGE_ID[] = "Vcpkg.VcpkgSettings"; +const char WEBSITE_URL[] = "https://vcpkg.io/"; +const char ENVVAR_VCPKG_ROOT[] = "VCPKG_ROOT"; +const char VCPKGMANIFEST_EDITOR_ID[] = "Vcpkg.VcpkgManifestEditor"; +const char VCPKGMANIFEST_MIMETYPE[] = "application/vcpkg.manifest+json"; + +} // namespace Vcpkg::Constants diff --git a/src/plugins/vcpkg/vcpkgmanifesteditor.cpp b/src/plugins/vcpkg/vcpkgmanifesteditor.cpp new file mode 100644 index 00000000000..fa7c6ebfaf3 --- /dev/null +++ b/src/plugins/vcpkg/vcpkgmanifesteditor.cpp @@ -0,0 +1,71 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "vcpkgmanifesteditor.h" + +#include "vcpkgconstants.h" +#include "vcpkgsearch.h" +#include "vcpkgsettings.h" +#include "vcpkgtr.h" + +#include + +#include + +#include + +#include + +namespace Vcpkg::Internal { + +class VcpkgManifestEditorWidget : public TextEditor::TextEditorWidget +{ +public: + VcpkgManifestEditorWidget() + { + m_searchPkgAction = toolBar()->addAction(Utils::Icons::ZOOM_TOOLBAR.icon(), + Tr::tr("Search package...")); + connect(m_searchPkgAction, &QAction::triggered, this, [this] { + const Search::VcpkgManifest package = Search::showVcpkgPackageSearchDialog(); + if (!package.name.isEmpty()) + textCursor().insertText(package.name); + }); + updateToolBar(); + + QAction *optionsAction = toolBar()->addAction(Utils::Icons::SETTINGS_TOOLBAR.icon(), + Core::ICore::msgShowOptionsDialog()); + connect(optionsAction, &QAction::triggered, [] { + Core::ICore::showOptionsDialog(Constants::TOOLSSETTINGSPAGE_ID); + }); + + connect(&VcpkgSettings::instance()->vcpkgRoot, &Utils::BaseAspect::changed, + this, &VcpkgManifestEditorWidget::updateToolBar); + } + + void updateToolBar() + { + m_searchPkgAction->setEnabled(VcpkgSettings::instance()->vcpkgRootValid()); + } + +private: + QAction *m_searchPkgAction; +}; + +static TextEditor::TextDocument *createVcpkgManifestDocument() +{ + auto doc = new TextEditor::TextDocument; + doc->setId(Constants::VCPKGMANIFEST_EDITOR_ID); + return doc; +} + +VcpkgManifestEditorFactory::VcpkgManifestEditorFactory() +{ + setId(Constants::VCPKGMANIFEST_EDITOR_ID); + setDisplayName(Tr::tr("Vcpkg Manifest Editor")); + addMimeType(Constants::VCPKGMANIFEST_MIMETYPE); + setDocumentCreator(createVcpkgManifestDocument); + setEditorWidgetCreator([] { return new VcpkgManifestEditorWidget; }); + setUseGenericHighlighter(true); +} + +} // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgmanifesteditor.h b/src/plugins/vcpkg/vcpkgmanifesteditor.h new file mode 100644 index 00000000000..c7762d69df9 --- /dev/null +++ b/src/plugins/vcpkg/vcpkgmanifesteditor.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace Vcpkg::Internal { + +class VcpkgManifestEditorFactory : public TextEditor::TextEditorFactory +{ +public: + VcpkgManifestEditorFactory(); +}; + +} // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgplugin.cpp b/src/plugins/vcpkg/vcpkgplugin.cpp new file mode 100644 index 00000000000..883b6896a72 --- /dev/null +++ b/src/plugins/vcpkg/vcpkgplugin.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "vcpkgplugin.h" + +#ifdef WITH_TESTS +#include "vcpkg_test.h" +#endif // WITH_TESTS +#include "vcpkgmanifesteditor.h" +#include "vcpkgsettings.h" + +#include + +namespace Vcpkg::Internal { + +class VcpkgPluginPrivate +{ +public: + VcpkgManifestEditorFactory manifestEditorFactory; + VcpkgSettingsPage settingsPage; +}; + +VcpkgPlugin::~VcpkgPlugin() +{ + delete d; +} + +void VcpkgPlugin::initialize() +{ + d = new VcpkgPluginPrivate; + ProjectExplorer::JsonWizardFactory::addWizardPath(":/vcpkg/wizards/"); + +#ifdef WITH_TESTS + addTest(); +#endif +} + +} // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgplugin.h b/src/plugins/vcpkg/vcpkgplugin.h new file mode 100644 index 00000000000..797083ea956 --- /dev/null +++ b/src/plugins/vcpkg/vcpkgplugin.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace ProjectExplorer { class Project; } + +namespace Vcpkg::Internal { + +class VcpkgPlugin final : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Vcpkg.json") + +public: + ~VcpkgPlugin(); + + void initialize() final; + +private: + class VcpkgPluginPrivate *d = nullptr; +}; + +} // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgsearch.cpp b/src/plugins/vcpkg/vcpkgsearch.cpp new file mode 100644 index 00000000000..dc3fe7a0c6c --- /dev/null +++ b/src/plugins/vcpkg/vcpkgsearch.cpp @@ -0,0 +1,214 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "vcpkgsearch.h" + +#include "qpushbutton.h" +#include "vcpkgsettings.h" +#include "vcpkgtr.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace Utils; + +namespace Vcpkg::Internal::Search { + +class VcpkgPackageSearchDialog : public QDialog +{ +public: + explicit VcpkgPackageSearchDialog(QWidget *parent); + + VcpkgManifest selectedPackage() const; + +private: + void listPackages(const QString &filter); + void showPackageDetails(const QString &packageName); + + VcpkgManifests m_allPackages; + VcpkgManifest m_selectedPackage; + + FancyLineEdit *m_packagesFilter; + ListWidget *m_packagesList; + QLineEdit *m_vcpkgName; + QLabel *m_vcpkgVersion; + QLabel *m_vcpkgLicense; + QTextBrowser *m_vcpkgDescription; + QLabel *m_vcpkgHomepage; + QDialogButtonBox *m_buttonBox; +}; + +VcpkgPackageSearchDialog::VcpkgPackageSearchDialog(QWidget *parent) + : QDialog(parent) +{ + resize(920, 400); + + m_packagesFilter = new FancyLineEdit; + m_packagesFilter->setFiltering(true); + m_packagesFilter->setFocus(); + m_packagesFilter->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + + m_packagesList = new ListWidget; + m_packagesList->setMaximumWidth(300); + + m_vcpkgName = new QLineEdit; + m_vcpkgName->setReadOnly(true); + + m_vcpkgVersion = new QLabel; + m_vcpkgLicense = new QLabel; + m_vcpkgDescription = new QTextBrowser; + + m_vcpkgHomepage = new QLabel; + m_vcpkgHomepage->setOpenExternalLinks(true); + m_vcpkgHomepage->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); + m_vcpkgHomepage->setTextInteractionFlags(Qt::TextBrowserInteraction); + + m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close); + + using namespace Utils::Layouting; + Column { + Row { + Column { + m_packagesFilter, + m_packagesList, + }, + Form { + Tr::tr("Name:"), m_vcpkgName, br, + Tr::tr("Version:"), m_vcpkgVersion, br, + Tr::tr("License:"), m_vcpkgLicense, br, + Tr::tr("Description:"), m_vcpkgDescription, br, + Tr::tr("Homepage:"), m_vcpkgHomepage, br, + }, + }, + m_buttonBox, + }.attachTo(this); + + m_allPackages = vcpkgManifests(VcpkgSettings::instance()->vcpkgRoot.filePath()); + + listPackages({}); + + connect(m_packagesFilter, &FancyLineEdit::filterChanged, + this, &VcpkgPackageSearchDialog::listPackages); + connect(m_packagesList, &ListWidget::currentTextChanged, + this, &VcpkgPackageSearchDialog::showPackageDetails); + connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +VcpkgManifest VcpkgPackageSearchDialog::selectedPackage() const +{ + return m_selectedPackage; +} + +void VcpkgPackageSearchDialog::listPackages(const QString &filter) +{ + const VcpkgManifests filteredPackages = filtered(m_allPackages, + [&filter] (const VcpkgManifest &package) { + return filter.isEmpty() + || package.name.contains(filter, Qt::CaseInsensitive) + || package.shortDescription.contains(filter, Qt::CaseInsensitive) + || package.description.contains(filter, Qt::CaseInsensitive); + }); + QStringList names = transform(filteredPackages, [] (const VcpkgManifest &package) { + return package.name; + }); + names.sort(); + m_packagesList->clear(); + m_packagesList->addItems(names); +} + +void VcpkgPackageSearchDialog::showPackageDetails(const QString &packageName) +{ + const VcpkgManifest manifest = findOrDefault(m_allPackages, + [&packageName] (const VcpkgManifest &m) { + return m.name == packageName; + }); + + m_vcpkgName->setText(manifest.name); + m_vcpkgVersion->setText(manifest.version); + m_vcpkgLicense->setText(manifest.license); + QString description = manifest.shortDescription; + if (!manifest.description.isEmpty()) + description.append("

" + manifest.description.join("

") + "

"); + m_vcpkgDescription->setText(description); + m_vcpkgHomepage->setText(QString::fromLatin1("%1") + .arg(manifest.homepage.toDisplayString())); + + m_selectedPackage = manifest; + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!manifest.name.isEmpty()); +} + +VcpkgManifest parseVcpkgManifest(const QByteArray &vcpkgManifestJsonData, bool *ok) +{ + // https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-json + VcpkgManifest result; + const QJsonObject jsonObject = QJsonDocument::fromJson(vcpkgManifestJsonData).object(); + if (const QJsonValue name = jsonObject.value("name"); !name.isUndefined()) + result.name = name.toString(); + for (const char *key : {"version", "version-semver", "version-date", "version-string"} ) { + if (const QJsonValue ver = jsonObject.value(QLatin1String(key)); !ver.isUndefined()) { + result.version = ver.toString(); + break; + } + } + if (const QJsonValue license = jsonObject.value("license"); !license.isUndefined()) + result.license = license.toString(); + if (const QJsonValue description = jsonObject.value("description"); !description.isUndefined()) { + if (description.isArray()) { + const QJsonArray descriptionLines = description.toArray(); + for (const QJsonValue &val : descriptionLines) { + const QString line = val.toString(); + if (result.shortDescription.isEmpty()) { + result.shortDescription = line; + continue; + } + result.description.append(line); + } + } else { + result.shortDescription = description.toString(); + } + } + if (const QJsonValue homepage = jsonObject.value("homepage"); !homepage.isUndefined()) + result.homepage = QUrl::fromUserInput(homepage.toString()); + + if (ok) + *ok = !(result.name.isEmpty() || result.version.isEmpty()); + + return result; +} + +VcpkgManifests vcpkgManifests(const FilePath &vcpkgRoot) +{ + const FilePath portsDir = vcpkgRoot / "ports"; + VcpkgManifests result; + const FilePaths manifestFiles = + portsDir.dirEntries({{"vcpkg.json"}, QDir::Files, QDirIterator::Subdirectories}); + for (const FilePath &manifestFile : manifestFiles) { + FileReader reader; + if (reader.fetch(manifestFile)) { + const QByteArray &manifestData = reader.data(); + const VcpkgManifest manifest = parseVcpkgManifest(manifestData); + result.append(manifest); + } + } + return result; +} + +VcpkgManifest showVcpkgPackageSearchDialog(QWidget *parent) +{ + VcpkgPackageSearchDialog dlg(parent ? parent : Core::ICore::dialogParent()); + return (dlg.exec() == QDialog::Accepted) ? dlg.selectedPackage() : VcpkgManifest(); +} + +} // namespace Vcpkg::Internal::Search diff --git a/src/plugins/vcpkg/vcpkgsearch.h b/src/plugins/vcpkg/vcpkgsearch.h new file mode 100644 index 00000000000..bb2d568a00c --- /dev/null +++ b/src/plugins/vcpkg/vcpkgsearch.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +#include + +namespace Vcpkg::Internal::Search { + +struct VcpkgManifest +{ + QString name; + QString version; + QString license; + QString shortDescription; + QStringList description; + QUrl homepage; +}; + +using VcpkgManifests = QList; + +VcpkgManifest parseVcpkgManifest(const QByteArray &vcpkgManifestJsonData, bool *ok = nullptr); +VcpkgManifests vcpkgManifests(const Utils::FilePath &vcpkgRoot); +VcpkgManifest showVcpkgPackageSearchDialog(QWidget *parent = nullptr); + +} // namespace Vcpkg::Internal::Search diff --git a/src/plugins/vcpkg/vcpkgsettings.cpp b/src/plugins/vcpkg/vcpkgsettings.cpp new file mode 100644 index 00000000000..bea6c8ed6c7 --- /dev/null +++ b/src/plugins/vcpkg/vcpkgsettings.cpp @@ -0,0 +1,80 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "vcpkgsettings.h" + +#include "vcpkgconstants.h" + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +namespace Vcpkg::Internal { + +VcpkgSettings::VcpkgSettings() +{ + setSettingsGroup("Vcpkg"); + + registerAspect(&vcpkgRoot); + vcpkgRoot.setSettingsKey("VcpkgRoot"); + vcpkgRoot.setDisplayStyle(Utils::StringAspect::PathChooserDisplay); + vcpkgRoot.setExpectedKind(Utils::PathChooser::ExistingDirectory); + vcpkgRoot.setDefaultValue(Utils::qtcEnvironmentVariable(Constants::ENVVAR_VCPKG_ROOT)); + + readSettings(Core::ICore::settings()); +} + +VcpkgSettings *VcpkgSettings::instance() +{ + static VcpkgSettings s; + return &s; +} + +bool VcpkgSettings::vcpkgRootValid() const +{ + return (vcpkgRoot.filePath() / "vcpkg").withExecutableSuffix().isExecutableFile(); +} + +VcpkgSettingsPage::VcpkgSettingsPage() +{ + setId(Constants::TOOLSSETTINGSPAGE_ID); + setDisplayName("Vcpkg"); + setCategory(CMakeProjectManager::Constants::Settings::CATEGORY); + + setLayouter([] (QWidget *widget) { + auto websiteButton = new QToolButton; + websiteButton->setIcon(Utils::Icons::ONLINE.icon()); + websiteButton->setToolTip(Constants::WEBSITE_URL); + + using namespace Utils::Layouting; + Column { + Group { + title(tr("Vcpkg installation")), + Form { + Utils::PathChooser::label(), + Span{ 2, Row{ VcpkgSettings::instance()->vcpkgRoot, websiteButton} }, + }, + }, + st, + }.attachTo(widget); + + connect(websiteButton, &QAbstractButton::clicked, [] { + QDesktopServices::openUrl(QUrl::fromUserInput(Constants::WEBSITE_URL)); + }); + }); +} + +void VcpkgSettingsPage::apply() +{ + VcpkgSettings::instance()->writeSettings(Core::ICore::settings()); +} + +} // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgsettings.h b/src/plugins/vcpkg/vcpkgsettings.h new file mode 100644 index 00000000000..a0a7973cc01 --- /dev/null +++ b/src/plugins/vcpkg/vcpkgsettings.h @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +namespace Vcpkg::Internal { + +class VcpkgSettings : public Utils::AspectContainer +{ +public: + VcpkgSettings(); + + static VcpkgSettings *instance(); + bool vcpkgRootValid() const; + + Utils::StringAspect vcpkgRoot; +}; + +class VcpkgSettingsPage final : public Core::IOptionsPage +{ +public: + VcpkgSettingsPage(); + + void apply() override; +}; + +} // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgtr.h b/src/plugins/vcpkg/vcpkgtr.h new file mode 100644 index 00000000000..0914ce6444d --- /dev/null +++ b/src/plugins/vcpkg/vcpkgtr.h @@ -0,0 +1,15 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace Vcpkg { + +struct Tr +{ + Q_DECLARE_TR_FUNCTIONS(Vcpkg) +}; + +} // namespace Vcpkg diff --git a/src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl b/src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl new file mode 100644 index 00000000000..1519949d2ea --- /dev/null +++ b/src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl @@ -0,0 +1,6 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", + "name": "%{Name}", + "version-string": "%{VersionString}", +%{Dependencies} +} diff --git a/src/plugins/vcpkg/wizards/manifest/wizard.json b/src/plugins/vcpkg/wizards/manifest/wizard.json new file mode 100644 index 00000000000..80e8b0fcc52 --- /dev/null +++ b/src/plugins/vcpkg/wizards/manifest/wizard.json @@ -0,0 +1,80 @@ +{ + "version": 1, + "supportedProjectTypes": [ ], + "id": "VcpkgManifest.Json", + "category": "U.VcpkgManifest", + "trDescription": "Creates a vcpkg.json manifest file.", + "trDisplayName": "vcpkg.json Manifest File", + "trDisplayCategory": "vcpkg", + "iconText": "json", + + "options": [ + { "key": "InitialFileName", "value": "vcpkg.json" }, + { "key": "TargetPath", "value": "%{Path}" } + ], + + "pages": + [ + { + "trDisplayName": "Location", + "trShortTitle": "Location", + "typeId": "File" + }, + { + "trDisplayName": "vcpkg.json Manifest File", + "trShortTitle": "Manifest fields", + "typeId": "Fields", + "data": + [ + { + "name": "Name", + "trDisplayName": "Name:", + "mandatory": true, + "type": "LineEdit", + "data": + { + "trText": "mypackage", + "validator": "^[a-z_0-9]+$" + } + }, + { + "name": "VersionString", + "trDisplayName": "Version string:", + "mandatory": true, + "type": "LineEdit", + "data": + { + "trText": "0.0.1" + } + }, + { + "name": "Dependencies", + "trDisplayName": "Dependencies:", + "mandatory": false, + "type": "TextEdit", + "data": + { + "trText": " \"dependencies\": [\n \"fmt\"\n ]" + } + } + ] + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": + [ + { + "typeId": "File", + "data": + { + "source": "vcpkg.json.tpl", + "target": "%{Path}/vcpkg.json", + "openInEditor": true + } + } + ] +}